#!/usr/bin/env python3
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (c) 2026 sol pbc
from __future__ import annotations

import argparse
import hashlib
import json
import sys
from pathlib import Path
from typing import Any

try:
    from scripts.build_native_sol_inventory import (
        FINAL_JOURNAL_PYTHON_COMPAT_TOTAL,
        ORACLE_PATH,
        REPO_ROOT,
        AuthorityEntry,
        check_oracle_subset,
        collect_oracle_paths,
        discover,
        format_paths,
    )
except ModuleNotFoundError:  # pragma: no cover - direct script execution path.
    from build_native_sol_inventory import (  # type: ignore[no-redef]
        FINAL_JOURNAL_PYTHON_COMPAT_TOTAL,
        ORACLE_PATH,
        REPO_ROOT,
        AuthorityEntry,
        check_oracle_subset,
        collect_oracle_paths,
        discover,
        format_paths,
    )

SCHEMA = "native-sol-authority-grammar-v1"


def authority_projection(entries: list[AuthorityEntry]) -> list[dict[str, Any]]:
    return [
        {
            "path": list(entry.path),
            "kind": entry.kind,
            "help": entry.help,
            "params": entry.params,
        }
        for entry in sorted(entries, key=lambda item: item.path)
        if entry.surface == "sol-call"
    ]


def build_projection(root: Path) -> tuple[bytes, list[str]]:
    entries = [entry for entry in discover(root) if entry.surface == "sol-call"]
    projection = authority_projection(entries)
    errors: list[str] = []
    if not entries:
        errors.append("native sol authority scan is empty")
    if not projection:
        errors.append("native sol authority grammar projection is empty")
    errors.extend(reconcile_against_oracle(entries))
    payload = {"schema": SCHEMA, "entries": projection}
    data = (
        json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
        + "\n"
    ).encode("utf-8")
    return data, errors


def reconcile_against_oracle(entries: list[AuthorityEntry]) -> list[str]:
    errors = check_oracle_subset(entries, ORACLE_PATH)
    oracle_errors, oracle_paths = collect_oracle_paths(ORACLE_PATH)
    errors.extend(oracle_errors)
    authority_paths = {entry.path for entry in entries if entry.surface == "sol-call"}
    if not authority_paths:
        return errors
    extra = sorted(authority_paths - oracle_paths)
    if extra:
        errors.append(f"authority paths outside frozen oracle: {format_paths(extra)}")
    uncovered = sorted(oracle_paths - authority_paths)
    non_journal_uncovered = [path for path in uncovered if path[0] != "journal"]
    if non_journal_uncovered:
        errors.append(
            "non-journal oracle paths lack native authority: "
            f"{format_paths(non_journal_uncovered)}"
        )
    if len(uncovered) != FINAL_JOURNAL_PYTHON_COMPAT_TOTAL:
        errors.append(
            "frozen oracle remainder count "
            f"{len(uncovered)} != {FINAL_JOURNAL_PYTHON_COMPAT_TOTAL}"
        )
    if not uncovered:
        errors.append("frozen oracle remainder is empty")
    return errors


def describe(data: bytes) -> str:
    payload = json.loads(data)
    return (
        f"entries={len(payload['entries'])} bytes={len(data)} "
        f"sha256={hashlib.sha256(data).hexdigest()}"
    )


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Build and check the authority-derived native sol grammar."
    )
    parser.add_argument("--root", type=Path, default=REPO_ROOT)
    parser.add_argument(
        "--output",
        type=Path,
        help="Optional path for the derived authority projection.",
    )
    parser.add_argument(
        "--check",
        action="store_true",
        help="Check authority projection against the frozen grammar oracle.",
    )
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    data, errors = build_projection(args.root.resolve())
    if errors:
        print("native sol authority grammar check failed:", file=sys.stderr)
        for error in errors:
            print(f"- {error}", file=sys.stderr)
        return 1
    if args.output is not None:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_bytes(data)
        print(f"wrote {args.output}: {describe(data)}")
    elif args.check:
        print(f"native sol authority grammar ok: {describe(data)}")
    else:
        sys.stdout.buffer.write(data)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
