"""Map DPE RunManifest to abi-control-core RunManifest schema."""

from __future__ import annotations

import hashlib

from dpe.models.config import DPEConfig
from dpe.models.manifest import RunManifest

from ._run_id import to_core_run_id

_VERDICT_MAP: dict[str, str] = {
    "completed": "pass",
    "budget_exceeded": "warn",
}


def _map_verdict(stop_reason: str) -> str:
    if stop_reason in _VERDICT_MAP:
        return _VERDICT_MAP[stop_reason]
    if stop_reason.startswith("error"):
        return "fail"
    return "fail"


def _compute_intent_hash(config_hash: str) -> str:
    """Compute an intent gate hash from the DPE config hash.

    Uses sha256(config_hash) to produce a deterministic 64-char hex digest.
    """
    digest = hashlib.sha256(config_hash.encode("utf-8")).hexdigest()
    return f"sha256:{digest}"


def _get_timestamps(manifest: RunManifest) -> tuple[str, str]:
    """Extract started_at and completed_at ISO 8601 strings from stage records."""
    if manifest.stages:
        started = manifest.stages[0].started_at
        # Use the last stage's finished_at, falling back to started_at
        last = manifest.stages[-1]
        completed = last.finished_at or last.started_at
    else:
        started = completed = manifest.timestamp

    return (
        started.isoformat().replace("+00:00", "Z"),
        completed.isoformat().replace("+00:00", "Z"),
    )


def _build_summary(manifest: RunManifest) -> dict:
    """Compute summary counts from stage records."""
    total = len(manifest.stages)
    skipped = sum(1 for s in manifest.stages if s.skipped)
    failed = sum(1 for s in manifest.stages if s.error)
    passed = total - skipped - failed

    verdict = _map_verdict(manifest.stop_reason)
    blocker = 1 if verdict == "fail" else 0

    return {
        "total": total,
        "passed": passed,
        "failed": failed,
        "skipped": skipped,
        "blocker": blocker,
    }


def _build_notes(manifest: RunManifest) -> str:
    """Build the notes string with DPE provenance info."""
    parts = [
        f"dpe_run_id={manifest.run_id}",
        "source=run_manifest.json",
    ]
    if manifest.warnings:
        parts.append(f"warnings={'; '.join(manifest.warnings)}")
    return "; ".join(parts)


def build_core_manifest(
    manifest: RunManifest,
    config: DPEConfig,
) -> dict:
    """Build a canonical run_manifest dict conforming to run-manifest/1.0.

    The manifest_hash_jcs_sha256 field is NOT set here — it must be
    computed by the emitter after schema validation and before writing.
    """
    core_run_id = to_core_run_id(manifest.run_id)
    started_at, completed_at = _get_timestamps(manifest)

    mode = "dry-run" if config.simulation_mode else "production"

    return {
        "schema_version": "run-manifest/1.0",
        "run_id": core_run_id,
        "gate": "custom",
        "mode": mode,
        "trigger": "manual",
        "started_at": started_at,
        "completed_at": completed_at,
        "intent_gate_hash": _compute_intent_hash(manifest.config_hash),
        "verdict": _map_verdict(manifest.stop_reason),
        "summary": _build_summary(manifest),
        "notes": _build_notes(manifest),
        "hashing": {"jcs": {"status": "ok"}},
    }
