"""Smoke test mode — deterministic offline CI tests.

Verifies that --smoke produces identical, schema-valid canonical artifacts
across multiple runs with no external model calls.
"""

from __future__ import annotations

import json
from pathlib import Path

from abi_control_core.sdk.validate import validate_json

from dpe.engine.orchestrator import _SMOKE_EPOCH, _SMOKE_RUN_ID, Orchestrator
from dpe.models.config import DPEConfig

_IDEA = "Build a task management app"


def _smoke_config(output_dir: str) -> DPEConfig:
    return DPEConfig(smoke_mode=True, output_dir=output_dir)


def _run_smoke(tmp_path: Path) -> tuple[Path, DPEConfig]:
    cfg = _smoke_config(str(tmp_path))
    orch = Orchestrator(config=cfg)
    orch.run(_IDEA)
    run_dir = tmp_path / _SMOKE_RUN_ID
    return run_dir, cfg


class TestSmokeConfig:
    def test_smoke_forces_simulation(self) -> None:
        cfg = DPEConfig(smoke_mode=True)
        assert cfg.simulation_mode is True

    def test_smoke_mode_default_false(self) -> None:
        cfg = DPEConfig()
        assert cfg.smoke_mode is False

    def test_smoke_config_hash_deterministic(self) -> None:
        c1 = DPEConfig(smoke_mode=True)
        c2 = DPEConfig(smoke_mode=True)
        assert c1.config_hash() == c2.config_hash()


class TestSmokeRunId:
    def test_fixed_run_id(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        orch = Orchestrator(config=cfg)
        assert orch.run_id == _SMOKE_RUN_ID

    def test_run_dir_uses_fixed_id(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        assert run_dir.name == _SMOKE_RUN_ID
        assert run_dir.exists()


class TestSmokeTimestamps:
    def test_all_stage_timestamps_are_epoch(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        orch = Orchestrator(config=cfg)
        manifest = orch.run(_IDEA)
        for stage in manifest.stages:
            assert stage.started_at == _SMOKE_EPOCH
            if stage.finished_at is not None:
                assert stage.finished_at == _SMOKE_EPOCH

    def test_manifest_timestamp_is_epoch(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        orch = Orchestrator(config=cfg)
        manifest = orch.run(_IDEA)
        assert manifest.timestamp == _SMOKE_EPOCH

    def test_wall_time_is_zero(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        orch = Orchestrator(config=cfg)
        manifest = orch.run(_IDEA)
        assert manifest.budget_actuals.wall_time_seconds == 0.0

    def test_artifact_written_at_is_epoch(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        orch = Orchestrator(config=cfg)
        manifest = orch.run(_IDEA)
        for artifact in manifest.artifacts:
            assert artifact.written_at == _SMOKE_EPOCH


class TestSmokeCanonicalArtifacts:
    """Canonical artifacts must exist and pass schema validation."""

    def test_plan_graph_exists(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        assert (run_dir / "plan_graph.json").exists()

    def test_eval_suite_exists(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        assert (run_dir / "eval_suite.json").exists()

    def test_run_manifest_core_exists(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        assert (run_dir / "run_manifest.core.json").exists()

    def test_plan_graph_validates(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        data = json.loads((run_dir / "plan_graph.json").read_text())
        validate_json(data, "plan_graph.schema.json")

    def test_eval_suite_validates(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        data = json.loads((run_dir / "eval_suite.json").read_text())
        validate_json(data, "eval_suite.schema.json")

    def test_run_manifest_core_validates(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        data = json.loads((run_dir / "run_manifest.core.json").read_text())
        validate_json(data, "run_manifest.schema.json")

    def test_core_manifest_verdict_pass(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        data = json.loads((run_dir / "run_manifest.core.json").read_text())
        assert data["verdict"] == "pass"

    def test_core_manifest_mode_dry_run(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        data = json.loads((run_dir / "run_manifest.core.json").read_text())
        assert data["mode"] == "dry-run"


class TestSmokeDeterminism:
    """Two smoke runs with the same idea must produce byte-identical canonical artifacts."""

    def test_plan_graph_identical(self, tmp_path: Path) -> None:
        dir_a = tmp_path / "a"
        dir_b = tmp_path / "b"
        for d in (dir_a, dir_b):
            cfg = _smoke_config(str(d))
            Orchestrator(config=cfg).run(_IDEA)

        a = (dir_a / _SMOKE_RUN_ID / "plan_graph.json").read_text()
        b = (dir_b / _SMOKE_RUN_ID / "plan_graph.json").read_text()
        assert a == b

    def test_eval_suite_identical(self, tmp_path: Path) -> None:
        dir_a = tmp_path / "a"
        dir_b = tmp_path / "b"
        for d in (dir_a, dir_b):
            cfg = _smoke_config(str(d))
            Orchestrator(config=cfg).run(_IDEA)

        a = (dir_a / _SMOKE_RUN_ID / "eval_suite.json").read_text()
        b = (dir_b / _SMOKE_RUN_ID / "eval_suite.json").read_text()
        assert a == b

    def test_run_manifest_core_identical(self, tmp_path: Path) -> None:
        dir_a = tmp_path / "a"
        dir_b = tmp_path / "b"
        for d in (dir_a, dir_b):
            cfg = _smoke_config(str(d))
            Orchestrator(config=cfg).run(_IDEA)

        a = (dir_a / _SMOKE_RUN_ID / "run_manifest.core.json").read_text()
        b = (dir_b / _SMOKE_RUN_ID / "run_manifest.core.json").read_text()
        assert a == b

    def test_dpe_manifest_identical(self, tmp_path: Path) -> None:
        dir_a = tmp_path / "a"
        dir_b = tmp_path / "b"
        for d in (dir_a, dir_b):
            cfg = _smoke_config(str(d))
            Orchestrator(config=cfg).run(_IDEA)

        a = (dir_a / _SMOKE_RUN_ID / "run_manifest.json").read_text()
        b = (dir_b / _SMOKE_RUN_ID / "run_manifest.json").read_text()
        assert a == b

    def test_stage_artifacts_identical(self, tmp_path: Path) -> None:
        dir_a = tmp_path / "a"
        dir_b = tmp_path / "b"
        for d in (dir_a, dir_b):
            cfg = _smoke_config(str(d))
            Orchestrator(config=cfg).run(_IDEA)

        run_a = dir_a / _SMOKE_RUN_ID
        run_b = dir_b / _SMOKE_RUN_ID
        for f in run_a.glob("*.md"):
            assert f.read_text() == (run_b / f.name).read_text(), f.name

    def test_manifest_hash_identical(self, tmp_path: Path) -> None:
        dir_a = tmp_path / "a"
        dir_b = tmp_path / "b"
        for d in (dir_a, dir_b):
            cfg = _smoke_config(str(d))
            Orchestrator(config=cfg).run(_IDEA)

        a = json.loads(
            (dir_a / _SMOKE_RUN_ID / "run_manifest.core.json").read_text()
        )
        b = json.loads(
            (dir_b / _SMOKE_RUN_ID / "run_manifest.core.json").read_text()
        )
        assert a["manifest_hash_jcs_sha256"] == b["manifest_hash_jcs_sha256"]


class TestSmokeNativeArtifacts:
    """DPE-native artifacts should also be produced correctly in smoke mode."""

    def test_completes_successfully(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        manifest = Orchestrator(config=cfg).run(_IDEA)
        assert manifest.stop_reason == "completed"

    def test_no_warnings(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        manifest = Orchestrator(config=cfg).run(_IDEA)
        assert manifest.warnings == []

    def test_all_artifact_files_exist(self, tmp_path: Path) -> None:
        run_dir, _ = _run_smoke(tmp_path)
        expected = [
            "run_manifest.json",
            "artifact_index.json",
            "plan_graph.json",
            "eval_suite.json",
            "run_manifest.core.json",
        ]
        for name in expected:
            assert (run_dir / name).exists(), f"Missing: {name}"

    def test_adapter_is_simulation(self, tmp_path: Path) -> None:
        cfg = _smoke_config(str(tmp_path))
        manifest = Orchestrator(config=cfg).run(_IDEA)
        for stage in manifest.stages:
            if not stage.skipped:
                assert stage.adapter_used == "simulation"
