from __future__ import annotations

import argparse
import json
from datetime import UTC, datetime
from pathlib import Path
from typing import Any


def read_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8"))


def rule_status(rule: dict[str, Any]) -> str:
    confidence = str(rule.get("confidence") or "none")
    evidence = rule.get("evidence") if isinstance(rule.get("evidence"), dict) else {}
    ok = evidence.get("ok")
    total = evidence.get("total")
    if confidence == "high" and isinstance(ok, int) and isinstance(total, int) and total > 0 and ok == total:
        return "verified_read"
    if confidence == "high" and int(evidence.get("distinct_rectangular_samples") or 0) > 0 and int(evidence.get("samples") or 0) > 0:
        return "verified_read"
    if confidence in {"medium", "high"}:
        return "candidate_read"
    return "needs_more_evidence"


def write_status(status: str) -> str:
    if status == "verified_read":
        return "blocked_until_roundtrip"
    return "blocked_until_verified_read"


def merge_named_range_analysis_rules(discovery: dict[str, Any], named_range_analysis: dict[str, Any] | None) -> dict[str, Any]:
    if not named_range_analysis:
        return discovery
    merged_rules = [rule for rule in discovery.get("rules") or [] if isinstance(rule, dict)]
    by_target = {str(rule.get("target") or ""): index for index, rule in enumerate(merged_rules)}
    for rule in named_range_analysis.get("rules") or []:
        if not isinstance(rule, dict) or not str(rule.get("target") or "").startswith("moxel.named_range."):
            continue
        target = str(rule.get("target") or "")
        promoted = {
            "id": rule.get("id") or target,
            "target": target,
            "expression": rule.get("expression"),
            "raw_scalar_indexes": rule.get("raw_scalar_indexes"),
            "confidence": rule.get("confidence") or "none",
            "evidence": rule.get("evidence") or {},
            "source": "named_range_analysis",
        }
        if target in by_target:
            existing = merged_rules[by_target[target]]
            confidence_order = {"none": 0, "low": 1, "medium": 2, "high": 3}
            if confidence_order.get(str(promoted.get("confidence")), 0) >= confidence_order.get(str(existing.get("confidence")), 0):
                merged_rules[by_target[target]] = {**existing, **promoted}
        else:
            by_target[target] = len(merged_rules)
            merged_rules.append(promoted)
    return {**discovery, "rules": merged_rules}


def build_registry(discovery: dict[str, Any], sources: list[str], named_range_analysis: dict[str, Any] | None = None) -> dict[str, Any]:
    discovery = merge_named_range_analysis_rules(discovery, named_range_analysis)
    registry_rules = []
    for index, rule in enumerate(discovery.get("rules") or [], start=1):
        if not isinstance(rule, dict):
            continue
        status = rule_status(rule)
        registry_rules.append(
            {
                "id": rule.get("id") or f"moxel_rule_{index}",
                "target": rule.get("target"),
                "expression": rule.get("expression"),
                "raw_scalar_indexes": rule.get("raw_scalar_indexes"),
                "confidence": rule.get("confidence") or "none",
                "read_status": status,
                "write_status": write_status(status),
                "evidence": rule.get("evidence") or {},
                "source_rule": rule,
            }
        )
    return {
        "schema": "codex_1c_moxel_schema_registry.v1",
        "generated_at": datetime.now(UTC).isoformat(timespec="seconds").replace("+00:00", "Z"),
        "sources": sources,
        "policy": {
            "read_use": "Only verified_read rules may be used as decoder behavior without additional diagnostics.",
            "write_use": "All MOXCEL write rules are blocked until a disposable-base round-trip proves exact behavior.",
        },
        "rules": registry_rules,
        "counts": {
            "rules": len(registry_rules),
            "verified_read": sum(1 for rule in registry_rules if rule.get("read_status") == "verified_read"),
            "candidate_read": sum(1 for rule in registry_rules if rule.get("read_status") == "candidate_read"),
            "write_enabled": sum(1 for rule in registry_rules if rule.get("write_status") == "verified_roundtrip"),
        },
    }


def render_markdown(registry: dict[str, Any]) -> str:
    lines = ["# 1C MOXCEL Schema Registry", ""]
    counts = registry.get("counts") or {}
    lines.append(f"- Rules: `{counts.get('rules')}`")
    lines.append(f"- Verified read: `{counts.get('verified_read')}`")
    lines.append(f"- Candidate read: `{counts.get('candidate_read')}`")
    lines.append(f"- Write enabled: `{counts.get('write_enabled')}`")
    lines.append("")
    lines.append("| Rule | Target | Read | Write | Confidence |")
    lines.append("| --- | --- | --- | --- | --- |")
    for rule in registry.get("rules") or []:
        lines.append(
            f"| `{rule.get('id')}` | `{rule.get('target')}` | `{rule.get('read_status')}` | "
            f"`{rule.get('write_status')}` | `{rule.get('confidence')}` |"
        )
    lines.append("")
    return "\n".join(lines)


def main() -> int:
    parser = argparse.ArgumentParser(description="Build the stable 1C MOXCEL schema registry from discovery reports.")
    parser.add_argument("--discovery", action="append", required=True, help="Discovery JSON. Repeatable; rules are merged in order.")
    parser.add_argument("--named-range-analysis", help="Optional named range rule analysis JSON.")
    parser.add_argument("--output-json", default="plugins/1c/metadata/moxel-schema-registry.json")
    parser.add_argument("--output-markdown", default="reports/1c-template-baselines/moxel-schema-registry.md")
    args = parser.parse_args()

    discoveries = [read_json(Path(path)) for path in args.discovery]
    merged = {"rules": []}
    seen: set[tuple[str, str]] = set()
    for discovery in discoveries:
        for rule in discovery.get("rules") or []:
            if not isinstance(rule, dict):
                continue
            key = (str(rule.get("id") or ""), str(rule.get("target") or ""))
            if key in seen:
                continue
            seen.add(key)
            merged["rules"].append(rule)
    named_range_analysis = read_json(Path(args.named_range_analysis)) if args.named_range_analysis else None
    sources = list(args.discovery)
    if args.named_range_analysis:
        sources.append(args.named_range_analysis)
    registry = build_registry(merged, sources, named_range_analysis)
    json_path = Path(args.output_json)
    md_path = Path(args.output_markdown)
    json_path.parent.mkdir(parents=True, exist_ok=True)
    md_path.parent.mkdir(parents=True, exist_ok=True)
    json_path.write_text(json.dumps(registry, ensure_ascii=False, indent=2), encoding="utf-8")
    md_path.write_text(render_markdown(registry), encoding="utf-8")
    print(json.dumps({"status": "ok", "json": str(json_path), "markdown": str(md_path), "counts": registry["counts"]}, ensure_ascii=False, indent=2))
    return 0


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