"""Gates: run checks and write gate_report.json (schema v1.0)."""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Any

from milco.core.run_context import RunContext
from milco.core.artifacts import (
    write_artifact,
    write_json_artifact,
    ensure_all_artifacts_exist,
)
from milco.tools.git_tools import git_available, is_repo_dirty
from milco.tools.exec_tools import run_command
from milco.policy.policy import run_policy_gate

GATE_SCHEMA_VERSION = "1.0"

# Canonical status strings
PASS = "PASS"
FAIL = "FAIL"
SKIP = "SKIP"


def _pytest_gate(ctx: RunContext) -> dict[str, Any]:
    """Run pytest if in apply mode, else skip."""
    if not ctx.can_apply():
        return {
            "name": "pytest",
            "status": SKIP,
            "details": "Dry-run: pytest execution skipped.",
        }
    result = run_command(
        "pytest", ["-q", "--tb=short"], cwd=ctx.repo_root, apply_mode=True
    )
    if result.blocked:
        return {"name": "pytest", "status": SKIP, "details": result.stderr}
    status = PASS if result.returncode == 0 else FAIL
    return {
        "name": "pytest",
        "status": status,
        "details": f"returncode={result.returncode}",
        "stdout": result.stdout[:2000],
        "stderr": result.stderr[:2000],
    }


def _repo_sanity_gate(ctx: RunContext) -> dict[str, Any]:
    """Check git cleanliness."""
    if not git_available(ctx.repo_root):
        return {"name": "repo_sanity", "status": SKIP, "details": "Git not available."}
    dirty = is_repo_dirty(ctx.repo_root)
    if ctx.apply_mode and dirty:
        return {
            "name": "repo_sanity",
            "status": FAIL,
            "details": "Repo is dirty before applying patch. Commit or stash first.",
        }
    return {"name": "repo_sanity", "status": PASS, "details": "Repo is clean."}


def _policy_gate(ctx: RunContext) -> dict[str, Any]:
    """Run all policy checks and summarise as a single gate entry."""
    diff_text = ""
    evidence_text = ""
    summary_text = ""

    diff_path = ctx.artifact_path("patches.diff")
    if diff_path.exists():
        diff_text = diff_path.read_text(encoding="utf-8")

    evidence_path = ctx.artifact_path("evidence.md")
    if evidence_path.exists():
        evidence_text = evidence_path.read_text(encoding="utf-8")

    summary_path = ctx.artifact_path("summary.md")
    if summary_path.exists():
        summary_text = summary_path.read_text(encoding="utf-8")

    checks = run_policy_gate(ctx.can_apply(), diff_text, evidence_text, summary_text)
    all_ok = checks.get("all_pass", False)
    return {
        "name": "policy",
        "status": PASS if all_ok else FAIL,
        "details": checks,
    }


def _gate_passed(g: dict[str, Any]) -> bool:
    """A gate passes if its status is PASS or SKIP."""
    return g.get("status") in (PASS, SKIP)


def run_gates(ctx: RunContext) -> dict[str, Any]:
    """Execute all gates. Returns the versioned report dict."""
    ctx.ensure_run_dir()

    gates_list: list[dict[str, Any]] = [
        _pytest_gate(ctx),
        _repo_sanity_gate(ctx),
        _policy_gate(ctx),
    ]

    all_pass = all(_gate_passed(g) for g in gates_list)
    overall = PASS if all_pass else FAIL
    exit_code = 0 if all_pass else 1

    report: dict[str, Any] = {
        "schema_version": GATE_SCHEMA_VERSION,
        "run_id": ctx.run_id,
        "mode": "apply" if ctx.can_apply() else "dry_run",
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "gates": gates_list,
        "overall_status": overall,
        "exit_code": exit_code,
        "errors": [g["details"] for g in gates_list if g["status"] == FAIL],
    }

    write_json_artifact(ctx, "gate_report.json", report)

    decision = "PASS" if all_pass else "FAIL"
    summary = (
        f"DECISION: {decision}\n\n"
        f"# Gate Summary\n\n"
        f"Run ID: `{ctx.run_id}`\n"
        f"Overall: **{overall}**\n\n"
    )
    for g in gates_list:
        summary += f"- **{g['name']}**: {g['status']}\n"

    write_artifact(ctx, "summary.md", summary)
    ensure_all_artifacts_exist(ctx)
    return report
