import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
SCRIPT = ROOT / "scripts" / "playbook.py"
MODE_ROOTS = {
    "snapshot": Path("custom/playbook"),
    "subtree": Path("docs/standards/playbook"),
}


def run_playbook(
    script: Path, config: Path, project_root: Path
) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        [sys.executable, str(script), "-config", str(config)],
        cwd=project_root,
        capture_output=True,
        text=True,
    )


def copy_subtree_source(destination: Path) -> None:
    destination.mkdir(parents=True)
    shutil.copy2(ROOT / ".gitattributes", destination / ".gitattributes")

    for name in ("scripts", "templates"):
        shutil.copytree(
            ROOT / name,
            destination / name,
            ignore=shutil.ignore_patterns("__pycache__", "*.pyc"),
        )

    (destination / "docs").mkdir()
    for name in ("common", "tsl", "markdown"):
        shutil.copytree(ROOT / "docs" / name, destination / "docs" / name)

    (destination / "rulesets").mkdir()
    shutil.copy2(
        ROOT / "rulesets" / "index.md",
        destination / "rulesets" / "index.md",
    )
    for name in ("tsl", "markdown"):
        shutil.copytree(
            ROOT / "rulesets" / name,
            destination / "rulesets" / name,
        )

    (destination / "skills").mkdir()
    for name in ("commit-message",):
        shutil.copytree(
            ROOT / "skills" / name,
            destination / "skills" / name,
        )


def write_config(project_root: Path, install_mode: str, playbook_root: Path) -> Path:
    config = project_root / "playbook.toml"
    config.write_text(
        f"""
[playbook]
project_root = "."
playbook_root = "{playbook_root.as_posix()}"
install_mode = "{install_mode}"

[sync_rules]
date = "2026-01-01"
no_backup = true

[sync_memory_bank]
project_name = "Demo"
no_backup = true

[sync_prompts]
no_backup = true

[sync_standards]
langs = ["tsl", "markdown"]
gitattr_mode = "overwrite"
no_backup = true

[install_skills]
agents_home = ".test-agents"
mode = "list"
skills = ["commit-message"]
no_backup = true
""".lstrip(),
        encoding="utf-8",
        newline="\n",
    )
    return config


def seed_custom_files(project_root: Path) -> None:
    custom_memory = project_root / "memory-bank" / "custom.md"
    custom_memory.parent.mkdir(parents=True)
    custom_memory.write_text("custom memory\n", encoding="utf-8", newline="\n")

    custom_prompt = project_root / "docs" / "prompts" / "custom.md"
    custom_prompt.parent.mkdir(parents=True)
    custom_prompt.write_text("custom prompt\n", encoding="utf-8", newline="\n")

    (project_root / "CLAUDE.md").write_text(
        "# Existing Claude\n\nKeep this.\n",
        encoding="utf-8",
        newline="\n",
    )


class PlaybookDeploymentTests(unittest.TestCase):
    def test_playbook_deployment_modes(self):
        for install_mode, playbook_root in MODE_ROOTS.items():
            with self.subTest(install_mode=install_mode):
                with tempfile.TemporaryDirectory() as tmp_dir:
                    project_root = Path(tmp_dir) / "project"
                    project_root.mkdir()

                    if install_mode == "subtree":
                        source_root = project_root / playbook_root
                        copy_subtree_source(source_root)
                        script = source_root / "scripts" / "playbook.py"
                    else:
                        source_root = ROOT
                        script = SCRIPT

                    seed_custom_files(project_root)
                    config = write_config(project_root, install_mode, playbook_root)

                    for run_number in (1, 2):
                        result = run_playbook(script, config, project_root)
                        self.assertEqual(
                            result.returncode,
                            0,
                            msg=(
                                f"{install_mode} run {run_number} failed\n"
                                f"{result.stdout}{result.stderr}"
                            ),
                        )

                    expected_paths = (
                        "AGENTS.md",
                        "AGENT_RULES.md",
                        "AGENT_RULES.local.md",
                        "CLAUDE.md",
                        ".gitattributes",
                        "memory-bank/project-brief.md",
                        "memory-bank/active-context.md",
                        "docs/prompts/system/agent-behavior.md",
                        ".agents/index.md",
                        ".agents/tsl/index.md",
                        ".agents/markdown/index.md",
                        ".test-agents/skills/commit-message/SKILL.md",
                        ".test-agents/skills/commit-message/references/commit_policy.json",
                        ".test-agents/skills/commit-message/scripts/validate_commit_message.py",
                    )
                    missing = [
                        path
                        for path in expected_paths
                        if not (project_root / path).exists()
                    ]
                    self.assertEqual(missing, [])

                    self.assertEqual(
                        (project_root / "memory-bank/custom.md").read_text(
                            encoding="utf-8"
                        ),
                        "custom memory\n",
                    )
                    self.assertEqual(
                        (project_root / "docs/prompts/custom.md").read_text(
                            encoding="utf-8"
                        ),
                        "custom prompt\n",
                    )

                    claude_text = (project_root / "CLAUDE.md").read_text(
                        encoding="utf-8"
                    )
                    self.assertIn("Keep this.", claude_text)
                    self.assertEqual(
                        claude_text.count("<!-- playbook:claude:start -->"), 1
                    )

                    docs_prefix = f"{playbook_root.as_posix()}/docs"
                    agents_index = (project_root / ".agents/index.md").read_text(
                        encoding="utf-8"
                    )
                    self.assertIn(f"- {docs_prefix}", agents_index)

                    source_skill = ROOT / "skills" / "commit-message"
                    installed_commit_skill = (
                        project_root / ".test-agents/skills/commit-message"
                    )
                    self.assertEqual(
                        (installed_commit_skill / "SKILL.md").read_text(encoding="utf-8"),
                        (source_skill / "SKILL.md").read_text(encoding="utf-8"),
                    )
                    self.assertEqual(
                        (
                            installed_commit_skill / "references/commit_policy.json"
                        ).read_text(encoding="utf-8"),
                        (source_skill / "references/commit_policy.json").read_text(
                            encoding="utf-8"
                        ),
                    )
                    self.assertEqual(
                        (
                            installed_commit_skill
                            / "scripts/validate_commit_message.py"
                        ).read_text(encoding="utf-8"),
                        (
                            source_skill / "scripts/validate_commit_message.py"
                        ).read_text(encoding="utf-8"),
                    )

                    rules_text = (project_root / "AGENT_RULES.md").read_text(
                        encoding="utf-8"
                    )
                    self.assertIn(
                        f"`{playbook_root.as_posix()}/` 是 Playbook 模板/供应商目录",
                        rules_text,
                    )

                    if install_mode == "snapshot":
                        snapshot_root = project_root / playbook_root
                        self.assertTrue((snapshot_root / "SOURCE.md").is_file())
                        self.assertTrue(
                            (snapshot_root / "scripts/playbook.py").is_file()
                        )
                        self.assertTrue((snapshot_root / "skills").is_dir())
                    else:
                        self.assertFalse((source_root / "SOURCE.md").exists())


if __name__ == "__main__":
    unittest.main()
