from pathlib import Path
import tomllib


ROOT = Path(__file__).resolve().parents[1]
AGENT_DIR = ROOT / ".codex" / "agents"
SKILL_DIR = ROOT / ".codex" / "skills"

EXPECTED_AGENTS = {
    "coordinator-agent",
    "correction-agent",
    "formulation-agent",
    "implementation-agent",
    "implementation-planning-agent",
    "io-definition-agent",
    "numerical-review-agent",
    "physics-evaluation-agent",
    "release-agent",
    "requirement-agent",
    "research-agent",
}

EXPECTED_SKILLS = {
    "fem-theory-query",
    "fesa-cpp-msvc-tdd",
    "fesa-formulation-spec",
    "fesa-io-contract",
    "fesa-numerical-review",
    "fesa-physics-sanity",
    "fesa-release-readiness",
    "fesa-requirements-baseline",
    "fesa-research-evidence",
}

LEGACY_OUTPUT_DIRS = {
    "coordination",
    "requirements",
    "research",
    "formulations",
    "numerical-reviews",
    "io-definitions",
    "reference-models",
    "implementation-plans",
    "build-test-reports",
    "corrections",
    "reference-verifications",
    "physics-evaluations",
    "releases",
}

FEATURE_FILES = {
    "linear-static-3d-euler-beam": {
        "coordination.md",
        "requirements.md",
        "research.md",
        "formulation.md",
        "numerical-review.md",
        "reference-model.md",
        "io.md",
        "implementation-plan.md",
        "implementation-report.md",
        "build-test.md",
        "reference-comparison.md",
        "physics-evaluation.md",
        "release.md",
    },
    "linear-static-mitc4-shell": {
        "coordination.md",
        "requirements.md",
        "research.md",
        "formulation.md",
        "numerical-review.md",
        "reference-model.md",
        "io.md",
        "implementation-plan.md",
        "build-test.md",
        "reference-comparison.md",
        "physics-evaluation.md",
        "release.md",
    },
}

LIVE_CONTRACT_FILES = (
    ROOT / "AGENTS.md",
    ROOT / "docs" / "SOLVER_AGENT_DESIGN.md",
    ROOT / "docs" / "SOLVER_SKILL_DESIGN.md",
)

RETIRED_TOKENS = (
    "reference-model-agent",
    "reference model agent",
    "build-test-executor-agent",
    "build/test executor agent",
    "reference-verification-agent",
    "reference verification agent",
    "fesa-reference-models",
    "fesa-reference-comparison",
    "docs/coordination/",
    "docs/requirements/",
    "docs/research/",
    "docs/formulations/",
    "docs/numerical-reviews/",
    "docs/io-definitions/",
    "docs/reference-models/",
    "docs/implementation-plans/",
    "docs/build-test-reports/",
    "docs/corrections/",
    "docs/reference-verifications/",
    "docs/physics-evaluations/",
    "docs/releases/",
)


def read(path: Path) -> str:
    return path.read_text(encoding="utf-8")


def live_contract_text() -> str:
    paths = list(LIVE_CONTRACT_FILES)
    paths.extend(sorted(AGENT_DIR.glob("*.toml")))
    paths.extend(sorted(SKILL_DIR.glob("*/SKILL.md")))
    return "\n".join(read(path) for path in paths)


def test_agent_inventory_is_consolidated():
    actual = {path.stem for path in AGENT_DIR.glob("*.toml")}
    assert actual == EXPECTED_AGENTS


def test_project_skill_inventory_is_consolidated():
    actual = {
        path.name
        for path in SKILL_DIR.iterdir()
        if path.is_dir() and (path / "SKILL.md").is_file()
    }
    assert actual == EXPECTED_SKILLS


def test_existing_feature_artifacts_are_bundled():
    docs_dir = ROOT / "docs"
    for legacy_dir in LEGACY_OUTPUT_DIRS:
        assert not (docs_dir / legacy_dir).exists(), legacy_dir

    for feature_id, expected_files in FEATURE_FILES.items():
        feature_dir = docs_dir / feature_id
        actual_files = {path.name for path in feature_dir.glob("*.md")}
        assert actual_files == expected_files


def test_numerical_review_absorbs_reference_model_contract():
    assert not (AGENT_DIR / "reference-model-agent.toml").exists()
    assert not (SKILL_DIR / "fesa-reference-models").exists()
    text = read(SKILL_DIR / "fesa-numerical-review" / "SKILL.md").lower()
    for marker in (
        "numerical-review.md",
        "reference-model.md",
        "reference case inventory",
        "source identity",
        "row precheck",
        "tolerance",
        "pass-for-io-definition",
    ):
        assert marker in text


def test_implementation_absorbs_build_test_and_reference_comparison():
    assert not (AGENT_DIR / "build-test-executor-agent.toml").exists()
    assert not (AGENT_DIR / "reference-verification-agent.toml").exists()
    assert not (SKILL_DIR / "fesa-reference-comparison").exists()
    text = read(SKILL_DIR / "fesa-cpp-msvc-tdd" / "SKILL.md").lower()
    for marker in (
        "implementation-report.md",
        "build-test.md",
        "reference-comparison.md",
        "artifact check -> compare -> classify -> report",
        "pass-for-physics-evaluation",
    ):
        assert marker in text


def test_agent_hierarchy_is_explicit():
    coordinator = read(AGENT_DIR / "coordinator-agent.toml").lower()
    assert "main agent" in coordinator
    assert "docs/<feature-id>/coordination.md" in coordinator
    assert "worklist" in coordinator
    assert "sub-agent dispatch" in coordinator

    for path in sorted(AGENT_DIR.glob("*.toml")):
        if path.name == "coordinator-agent.toml":
            continue
        text = read(path).lower()
        assert "sub-agent" in text, path.name
        assert "coordinator agent" in text, path.name


def test_live_contracts_have_no_retired_names_or_paths():
    text = live_contract_text().lower()
    assert "docs/<feature-id>/" in text
    for token in RETIRED_TOKENS:
        assert token not in text, token


def test_live_guidance_declares_eight_stage_workflow():
    for path in LIVE_CONTRACT_FILES:
        text = read(path)
        assert "8단계" in text, path
        assert "docs/<feature-id>/" in text, path


def test_agent_toml_and_skill_ui_metadata_are_parseable():
    for path in sorted(AGENT_DIR.glob("*.toml")):
        parsed = tomllib.loads(read(path))
        assert parsed["name"] == path.stem
        assert parsed["model_reasoning_effort"] == "extra high"

    for skill_name in EXPECTED_SKILLS:
        skill_dir = SKILL_DIR / skill_name
        skill_text = read(skill_dir / "SKILL.md")
        ui_text = read(skill_dir / "agents" / "openai.yaml")
        assert f"name: {skill_name}" in skill_text
        assert f"${skill_name}" in ui_text
