"""A3 engine tests — the generate -> sync cycle on a synthetic project fixture."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from mgf.fedres import SyncState, validate_payload
from mgf.fedres.generate import GenerateError, generate
from mgf.fedres.provider import DiskProvider
from mgf.fedres.sync import deep_check, shallow_check

PROJECT_TOML = """\
[project]
id = "demo"
name = "Demo Project"
kind = "library"
"""

STATUS_TOML = """\
[[tasks]]
id = "T-1"
title = "First task"
state = "done"

[[tasks]]
id = "T-2"
title = "Second task"
state = "next"
"""

ROADMAP_TOML = """\
final_stage = { id = "v1", label = "v1.0" }

[[stages]]
id = "s0"
label = "Stage 0"
state = "done"
progress = 1.0

[[stages]]
id = "v1"
label = "v1.0"
state = "planned"
progress = 0.0
depends_on = ["s0"]
"""

AT = "2026-06-29T00:00:00Z"


def _setup(root: Path, *, status: str = STATUS_TOML, roadmap: str = ROADMAP_TOML) -> DiskProvider:
    src = root / "fed-resources" / "source"
    src.mkdir(parents=True)
    (src / "project.toml").write_text(PROJECT_TOML, encoding="utf-8")
    (src / "status.toml").write_text(status, encoding="utf-8")
    (src / "roadmap.toml").write_text(roadmap, encoding="utf-8")
    return DiskProvider(root)


def test_generate_then_in_sync(tmp_path: Path) -> None:
    provider = _setup(tmp_path)
    generate(provider, generated_at=AT, write=True)
    assert shallow_check(provider).state is SyncState.IN_SYNC
    assert deep_check(provider).state is SyncState.IN_SYNC


def test_generated_files_validate(tmp_path: Path) -> None:
    provider = _setup(tmp_path)
    generate(provider, generated_at=AT, write=True)
    for relpath in provider.list_generated():
        payload = json.loads(provider.generated_bytes(relpath))
        assert validate_payload(payload) == [], f"{relpath} should validate"


def test_status_summary_is_computed(tmp_path: Path) -> None:
    provider = _setup(tmp_path)
    generate(provider, generated_at=AT, write=True)
    status = json.loads(provider.generated_bytes("status.json"))
    assert status["summary"]["total"] == 2
    assert status["summary"]["by_state"] == {"done": 1, "next": 1}


def test_generate_is_deterministic(tmp_path: Path) -> None:
    provider = _setup(tmp_path)
    a = generate(provider, generated_at=AT, write=False).files
    b = generate(provider, generated_at="2099-01-01T00:00:00Z", write=False).files
    assert a == b, "state files must be byte-identical regardless of generated_at"


def test_source_edit_makes_stale_then_regenerate_heals(tmp_path: Path) -> None:
    provider = _setup(tmp_path)
    generate(provider, generated_at=AT, write=True)
    # author adds a task to the source single-source
    (provider.source_dir / "status.toml").write_text(
        STATUS_TOML + '\n[[tasks]]\nid = "T-3"\ntitle = "Third"\nstate = "todo"\n', encoding="utf-8"
    )
    report = deep_check(provider)
    assert report.state is SyncState.STALE
    assert "status.json" in report.changed_files
    # force-sync heals it
    generate(provider, generated_at=AT, write=True)
    assert deep_check(provider).state is SyncState.IN_SYNC


def test_handedit_generated_is_tampered(tmp_path: Path) -> None:
    provider = _setup(tmp_path)
    generate(provider, generated_at=AT, write=True)
    target = provider.generated_dir / "status.json"
    target.write_text(
        target.read_text(encoding="utf-8").replace("First task", "HACKED"), encoding="utf-8"
    )
    assert shallow_check(provider).state is SyncState.TAMPERED
    assert deep_check(provider).state is SyncState.TAMPERED


def test_no_fedres_is_unknown(tmp_path: Path) -> None:
    provider = DiskProvider(tmp_path)
    assert shallow_check(provider).state is SyncState.UNKNOWN
    assert deep_check(provider).state is SyncState.UNKNOWN


def test_fedres_without_manifest_is_unreadable(tmp_path: Path) -> None:
    provider = _setup(tmp_path)  # source/ exists, but nothing generated yet
    assert shallow_check(provider).state is SyncState.UNREADABLE


def test_invalid_source_is_generate_error(tmp_path: Path) -> None:
    provider = _setup(
        tmp_path, status='[[tasks]]\nid = "T-1"\nstate = "blocked"\n'
    )  # blocked w/o reason, no title
    with pytest.raises(GenerateError):
        generate(provider, generated_at=AT, write=True)


def test_missing_project_toml_is_generate_error(tmp_path: Path) -> None:
    (tmp_path / "fed-resources" / "source").mkdir(parents=True)
    with pytest.raises(GenerateError):
        generate(DiskProvider(tmp_path), generated_at=AT, write=True)
