from __future__ import annotations

import json
import re
from datetime import datetime, timezone
from pathlib import Path
from types import SimpleNamespace

from recoil.core.paths import ProjectPaths
from recoil.pipeline.tools import migrate_f5_slug_merge as f5


def _make_project(tmp_path: Path) -> Path:
    project_root = tmp_path / "tartarus"
    loc_root = project_root / "assets" / "loc"
    corridor = loc_root / "corridor"
    corridor.mkdir(parents=True)
    (corridor / "stub.txt").write_text("stub\n")
    (corridor / "_LINEAGE.txt").write_text("header only\n")
    (corridor / "dangling.png").symlink_to("missing.png")

    canonical = loc_root / "int_lower_decks_corridor"
    canonical.mkdir(parents=True)
    (canonical / "KEEP.txt").write_text("canonical\n")

    (loc_root / "loc.json").write_text(json.dumps({"corridor": "stale"}) + "\n")
    return project_root


def _patch_paths(monkeypatch, project_root: Path) -> ProjectPaths:
    paths = ProjectPaths.from_root(project_root)
    monkeypatch.setattr(f5, "ProjectPaths", SimpleNamespace(for_project=lambda project: paths))
    return paths


def _receipt_files(paths: ProjectPaths) -> list[Path]:
    if not paths.history_migration_dir.exists():
        return []
    return sorted(paths.history_migration_dir.glob("f5_slug_merge_*.json"))


def test_f5_slug_merge_dry_run_prints_full_plan_and_writes_nothing(
    tmp_path, monkeypatch, capsys
):
    project_root = _make_project(tmp_path)
    paths = _patch_paths(monkeypatch, project_root)

    rc = f5.migrate("tartarus", dry_run=True)

    assert rc == 0
    out = capsys.readouterr().out
    assert "DRY RUN" in out
    assert "assets/loc/corridor/stub.txt" in out
    assert "assets/loc/corridor/dangling.png" in out
    assert "assets/loc/loc.json" in out
    assert (project_root / "assets" / "loc" / "corridor").exists()
    assert (project_root / "assets" / "loc" / "loc.json").exists()
    assert not _receipt_files(paths)


def test_f5_slug_merge_aborts_on_real_media_without_deleting_anything(
    tmp_path, monkeypatch, capsys
):
    project_root = _make_project(tmp_path)
    paths = _patch_paths(monkeypatch, project_root)
    big = project_root / "assets" / "loc" / "corridor" / "real_media.png"
    big.write_bytes(b"x" * (1024 * 1024))

    rc = f5.migrate("tartarus")

    assert rc == 1
    err = capsys.readouterr().err
    assert "ABORT" in err
    assert "assets/loc/corridor/real_media.png" in err
    assert big.exists()
    assert (project_root / "assets" / "loc" / "corridor").exists()
    assert (project_root / "assets" / "loc" / "loc.json").exists()
    assert not _receipt_files(paths)


def test_f5_slug_merge_real_run_receipt_shape_and_idempotency(
    tmp_path, monkeypatch, capsys
):
    project_root = _make_project(tmp_path)
    paths = _patch_paths(monkeypatch, project_root)
    now = datetime(2026, 6, 11, 12, 30, tzinfo=timezone.utc)

    rc = f5.migrate("tartarus", now=now)

    assert rc == 0
    assert not (project_root / "assets" / "loc" / "corridor").exists()
    assert not (project_root / "assets" / "loc" / "loc.json").exists()
    assert (project_root / "assets" / "loc" / "int_lower_decks_corridor" / "KEEP.txt").exists()

    receipts = _receipt_files(paths)
    assert [p.name for p in receipts] == ["f5_slug_merge_2026-06-11.json"]
    receipt = json.loads(receipts[0].read_text())
    assert receipt["schema_version"] == 1
    assert receipt["migration"] == "f5_slug_merge"
    assert receipt["created_at"] == now.isoformat()
    assert receipt["why"] == f5.WHY
    assert receipt["spec"].endswith("BUILD_SPEC_breakdown-c1c2.md#phase-6-f5-slug-merge")
    assert receipt["quarantine_policy"]["status"] == "untouched"
    assert "6 quarantined 2026-06-01 batch files" in receipt["quarantine_policy"]["note"]
    assert receipt["scope_boundary"]["canonical_location_untouched"] is True
    assert "internal hyphen/underscore stem drift" in receipt["scope_boundary"][
        "canonical_stem_drift_note"
    ]

    corridor_inventory = receipt["deleted_inventory"]["corridor_dir"]
    rel_paths = {item["path"] for item in corridor_inventory}
    assert "assets/loc/corridor/stub.txt" in rel_paths
    assert "assets/loc/corridor/_LINEAGE.txt" in rel_paths
    symlink_item = next(
        item for item in corridor_inventory if item["path"] == "assets/loc/corridor/dangling.png"
    )
    assert symlink_item["kind"] == "symlink"
    assert symlink_item["target"] == "missing.png"
    assert receipt["deleted_inventory"]["loc_index"][0]["path"] == "assets/loc/loc.json"

    capsys.readouterr()
    second = f5.migrate("tartarus", now=now)
    assert second == 0
    assert "no-op" in capsys.readouterr().out
    assert _receipt_files(paths) == receipts


def test_audit_fixture_slug_has_no_drifted_corridor_literal():
    audit_dispatch = Path("recoil/pipeline/tools/audit_dispatch.py").read_text()
    audit_plan = Path("recoil/pipeline/tools/audit_fixtures/audit_plan.json").read_text()

    assert re.search(r"""['"]corridor['"]""", audit_dispatch) is None
    assert '"corridor"' not in audit_plan
    assert "audit_fixture_corridor" in audit_dispatch
    assert "audit_fixture_corridor" in audit_plan
