#!/usr/bin/env python3
"""Generate an AI decision record from QoR evidence."""

from __future__ import annotations

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


def load_manifest(path: Path) -> dict[str, Any]:
    manifest = path / "manifest.json" if path.is_dir() else path
    return json.loads(manifest.read_text(encoding="utf-8"))


def decision_from_manifest(manifest: dict[str, Any]) -> dict[str, Any]:
    summary = manifest.get("summary", {})
    timing = summary.get("timing", {})
    top_fanout = summary.get("top_fanout", [])
    wns = timing.get("wns")
    tns = timing.get("tns")
    failing = timing.get("failing_endpoints")

    timing_met = bool(
        wns is not None
        and tns is not None
        and failing is not None
        and float(wns) >= 0
        and float(tns) == 0
        and int(failing) == 0
    )

    watch_items = []
    for item in top_fanout[:5]:
        watch_items.append(
            {
                "net": item.get("net"),
                "fanout": item.get("fanout"),
                "worst_slack_ns": item.get("worst_slack_ns"),
                "reason": "high fanout with limited slack"
                if item.get("worst_slack_ns") is not None and item.get("worst_slack_ns") < 0.5
                else "high fanout evidence",
            }
        )

    auto_optimize = not timing_met
    if timing_met:
        decision = "record_baseline_only"
        reason = "Timing is met; automatic optimization would disturb a working routed result without evidence of benefit."
        allowed = ["collect_reports", "compare_future_qor"]
    else:
        decision = "consider_low_risk_vivado_experiments"
        reason = "Timing is not met or incomplete; low-risk Vivado-only experiments may be considered after reviewing worst paths."
        allowed = [
            "collect_reports",
            "phys_opt_design_experiment",
            "route_design_experiment",
            "before_after_qor_compare",
        ]

    return {
        "created_at": datetime.now().isoformat(timespec="seconds"),
        "tool": "qor_decision",
        "source_manifest": manifest.get("artifacts", {}).get("out_dir"),
        "timing": {
            "met": timing_met,
            "wns": wns,
            "tns": tns,
            "failing_endpoints": failing,
            "total_endpoints": timing.get("total_endpoints"),
        },
        "decision": decision,
        "auto_optimize": auto_optimize,
        "reason": reason,
        "allowed_automatic_actions": allowed,
        "requires_human_authorization": [
            "latency_change",
            "axi_stream_protocol_change",
            "cdc_rdc_change",
            "clock_reset_architecture_change",
            "false_path_constraint",
            "multicycle_path_constraint",
            "ps_pl_handoff_semantics_change",
            "rf_data_alignment_change",
        ],
        "amd_doc_mcp": {
            "required_now": False,
            "reason": "No new tool-semantics or constraint change is being proposed.",
        },
        "vitis_ps_check": {
            "required_now": False,
            "reason": "No PL design or handoff change is being proposed.",
        },
        "watch_items": watch_items,
        "next_steps": [
            "Keep this QoR directory as baseline evidence.",
            "Run qor_compare.py against future QoR captures.",
            "Do not run optimization experiments unless timing regresses or a specific implementation issue is documented.",
        ]
        if timing_met
        else [
            "Review raw timing paths before selecting an experiment.",
            "Use AMD Doc MCP if choosing a directive or constraint whose semantics are uncertain.",
            "Run only one low-risk experiment at a time and compare against baseline.",
        ],
    }


def write_markdown(decision: dict[str, Any], path: Path) -> None:
    lines = [
        "# QoR Decision",
        "",
        f"- Decision: `{decision['decision']}`",
        f"- Auto optimize: `{str(decision['auto_optimize']).lower()}`",
        f"- Reason: {decision['reason']}",
        "",
        "## Timing",
        "",
        f"- Met: `{str(decision['timing']['met']).lower()}`",
        f"- WNS: `{decision['timing']['wns']}`",
        f"- TNS: `{decision['timing']['tns']}`",
        f"- Failing endpoints: `{decision['timing']['failing_endpoints']}`",
        f"- Total endpoints: `{decision['timing']['total_endpoints']}`",
        "",
        "## Watch Items",
        "",
    ]
    for item in decision["watch_items"]:
        lines.append(
            f"- `{item['net']}`: fanout `{item['fanout']}`, worst slack `{item['worst_slack_ns']}` ns, {item['reason']}"
        )
    lines.extend(["", "## Next Steps", ""])
    for step in decision["next_steps"]:
        lines.append(f"- {step}")
    lines.extend(["", "## Red Lines", ""])
    for item in decision["requires_human_authorization"]:
        lines.append(f"- `{item}`")
    path.write_text("\n".join(lines) + "\n", encoding="utf-8")


def main(argv: list[str]) -> int:
    parser = argparse.ArgumentParser(description="Generate QoR decision record")
    parser.add_argument("manifest_or_dir")
    args = parser.parse_args(argv)

    input_path = Path(args.manifest_or_dir).resolve()
    out_dir = input_path if input_path.is_dir() else input_path.parent
    decision = decision_from_manifest(load_manifest(input_path))
    (out_dir / "decision.json").write_text(json.dumps(decision, indent=2), encoding="utf-8")
    write_markdown(decision, out_dir / "decision.md")
    print(json.dumps(decision, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
