"""Orchestrate building, validating, and writing canonical artifacts."""

from __future__ import annotations

import json
import logging
from dataclasses import dataclass, field
from datetime import UTC, datetime
from pathlib import Path

from abi_control_core.sdk.canonical_hash import canonical_hash_jcs_sha256
from abi_control_core.sdk.validate import SchemaValidationError, validate_json

from dpe.engine.states import Stage
from dpe.models.config import DPEConfig
from dpe.models.manifest import RunManifest

from ._errors import CoreBridgeValidationError
from ._eval_suite import build_eval_suite
from ._plan_graph import build_plan_graph
from ._run_manifest_core import build_core_manifest

logger = logging.getLogger(__name__)

# Map artifact filenames to their core schema names.
_SCHEMA_MAP = {
    "plan_graph.json": "plan_graph.schema.json",
    "eval_suite.json": "eval_suite.schema.json",
    "run_manifest.core.json": "run_manifest.schema.json",
}


@dataclass
class EmissionResult:
    """Paths to the emitted canonical artifacts."""

    plan_graph_path: Path
    eval_suite_path: Path
    core_manifest_path: Path
    manifest_hash: str = ""
    validation_errors: list[dict] = field(default_factory=list)


def _write_json(path: Path, data: dict) -> None:
    path.write_text(
        json.dumps(data, indent=2, ensure_ascii=False),
        encoding="utf-8",
    )


def _write_validation_errors(run_dir: Path, errors: list[dict]) -> Path:
    path = run_dir / "validation_errors.json"
    payload = {
        "timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
        "errors": errors,
    }
    _write_json(path, payload)
    logger.warning("Validation errors written to %s", path)
    return path


def _validate_artifact(name: str, artifact: dict) -> list[dict]:
    """Validate a single artifact. Returns a list of error dicts (empty if valid)."""
    schema_name = _SCHEMA_MAP[name]
    try:
        validate_json(artifact, schema_name)
        return []
    except SchemaValidationError as e:
        return [{"path": ptr, "message": msg} for ptr, msg in e.errors]


def emit_core_artifacts(
    *,
    manifest: RunManifest,
    run_dir: Path,
    config: DPEConfig,
    stage_outputs: dict[Stage, str],
) -> EmissionResult:
    """Build, validate, and write all canonical artifacts for a DPE run.

    Writes to run_dir:
      - plan_graph.json
      - eval_suite.json
      - run_manifest.core.json

    On validation failure, writes validation_errors.json and raises
    CoreBridgeValidationError.
    """
    # 1. Build all three artifacts
    plan_graph = build_plan_graph(manifest)
    eval_suite = build_eval_suite(manifest, stage_outputs)
    core_manifest = build_core_manifest(manifest, config)

    # 2. Validate all three against core schemas
    all_errors: list[dict] = []
    for name, artifact in [
        ("plan_graph.json", plan_graph),
        ("eval_suite.json", eval_suite),
        ("run_manifest.core.json", core_manifest),
    ]:
        errors = _validate_artifact(name, artifact)
        if errors:
            all_errors.append({"file": name, "errors": errors})

    if all_errors:
        _write_validation_errors(run_dir, all_errors)
        raise CoreBridgeValidationError(
            f"Core artifact validation failed: "
            f"{len(all_errors)} artifact(s) invalid",
            errors=all_errors,
        )

    # 3. Compute manifest hash AFTER validation, BEFORE writing
    manifest_hash = canonical_hash_jcs_sha256(core_manifest)
    core_manifest["manifest_hash_jcs_sha256"] = f"jcs_sha256:{manifest_hash}"

    # 4. Write all artifacts
    pg_path = run_dir / "plan_graph.json"
    es_path = run_dir / "eval_suite.json"
    cm_path = run_dir / "run_manifest.core.json"

    _write_json(pg_path, plan_graph)
    _write_json(es_path, eval_suite)
    _write_json(cm_path, core_manifest)

    logger.info(
        "Core artifacts emitted: plan_graph, eval_suite, run_manifest.core "
        "(hash=%s…)",
        manifest_hash[:12],
    )

    return EmissionResult(
        plan_graph_path=pg_path,
        eval_suite_path=es_path,
        core_manifest_path=cm_path,
        manifest_hash=manifest_hash,
    )
