"""Optional promotion-readiness evaluation step.

Detects a ``promotion_candidate.json`` artifact in an evidence pack or run
directory.  When present, invokes ``abi-promotion verify-candidate`` as a
subprocess and stores the resulting ``promotion_receipt.json`` next to other
eval artifacts.

This module is **report-only** — it never blocks a run.  The promotion status
(pass / warn / fail) is surfaced for informational purposes only.

No hard dependency on the ``abi_promotion`` package: verification happens
entirely via subprocess so the dependency direction rule is preserved.
"""
from __future__ import annotations

import json
import subprocess
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any

CANDIDATE_FILENAME = "promotion_candidate.json"
RECEIPT_FILENAME = "promotion_receipt.json"


@dataclass
class PromotionGateResult:
    """Summary of the promotion-readiness check."""

    status: str  # pass | warn | fail | skipped | error
    receipt_path: str | None = None
    message: str = ""
    receipt: dict[str, Any] | None = None

    def to_dict(self) -> dict[str, Any]:
        d: dict[str, Any] = {"promotion": {"status": self.status, "message": self.message}}
        if self.receipt_path:
            d["promotion"]["receipt_path"] = self.receipt_path
        if self.receipt:
            d["promotion"]["receipt_summary"] = {
                "receipt_version": self.receipt.get("receipt_version"),
                "candidate_hash": self.receipt.get("candidate_hash"),
                "status": self.receipt.get("status"),
                "checks_count": len(self.receipt.get("checks", [])),
                "violations_count": len(self.receipt.get("violations", [])),
            }
        return d


def find_candidate(evidence_dir: Path) -> Path | None:
    """Return path to promotion_candidate.json if it exists under *evidence_dir*."""
    candidate = evidence_dir / CANDIDATE_FILENAME
    if candidate.is_file():
        return candidate
    return None


def run_verify_candidate(
    candidate_path: Path,
    output_dir: Path,
    *,
    python: str | None = None,
) -> tuple[dict[str, Any] | None, str, str]:
    """Invoke ``abi-promotion verify-candidate`` as a subprocess.

    Returns ``(receipt_dict | None, stdout, stderr)``.
    """
    py = python or sys.executable
    cmd = [
        py, "-m", "abi_promotion",
        "verify-candidate", str(candidate_path),
        "--out", str(output_dir),
    ]

    result = subprocess.run(cmd, capture_output=True, text=True)

    receipt_path = output_dir / RECEIPT_FILENAME
    receipt: dict[str, Any] | None = None
    if receipt_path.is_file():
        receipt = json.loads(receipt_path.read_text(encoding="utf-8"))

    return receipt, result.stdout, result.stderr


def evaluate_promotion(
    evidence_dir: Path,
    output_dir: Path | None = None,
    *,
    python: str | None = None,
) -> PromotionGateResult:
    """Run the promotion-readiness check against *evidence_dir*.

    If ``promotion_candidate.json`` is absent the check is cleanly skipped.
    If present, ``abi-promotion verify-candidate`` is executed and the receipt
    stored in *output_dir* (defaults to *evidence_dir*).

    This function never raises — errors are captured into the result.
    """
    candidate_path = find_candidate(evidence_dir)
    if candidate_path is None:
        return PromotionGateResult(
            status="skipped",
            message="No promotion_candidate.json found; skipping promotion gate.",
        )

    out = output_dir or evidence_dir
    out.mkdir(parents=True, exist_ok=True)

    try:
        receipt, stdout, stderr = run_verify_candidate(
            candidate_path, out, python=python,
        )
    except FileNotFoundError:
        return PromotionGateResult(
            status="error",
            message="abi-promotion is not installed or not on PATH.",
        )
    except Exception as exc:
        return PromotionGateResult(
            status="error",
            message=f"Subprocess failed: {exc}",
        )

    if receipt is None:
        return PromotionGateResult(
            status="error",
            message=f"verify-candidate produced no receipt. stderr: {stderr.strip()}",
        )

    receipt_file = out / RECEIPT_FILENAME
    return PromotionGateResult(
        status=receipt.get("status", "error"),
        receipt_path=str(receipt_file),
        message=f"verify-candidate completed with status={receipt.get('status')}.",
        receipt=receipt,
    )
