"""Tests for the scaffolding engine."""

import json
from pathlib import Path

from tokenguard.scaffold import (
    generate_claude_md,
    generate_mirror_json,
    merge_hook_into_settings,
    scaffold_project,
)


def test_generate_claude_md_standalone():
    content = generate_claude_md("python", grip_global=False, project_name="myapp")
    assert "myapp" in content
    assert "python" in content.lower()
    assert "Safety Gates" in content
    assert "Dependency Guardian" in content


def test_generate_claude_md_grip_user():
    content = generate_claude_md("python", grip_global=True, project_name="myapp")
    assert "myapp" in content
    # Should NOT duplicate rules when GRIP is global
    assert "Global rules inherited" in content


def test_merge_hook_into_settings_new_file(tmp_path):
    settings_path = tmp_path / "settings.json"
    content = merge_hook_into_settings(settings_path, "python3 ~/.claude/hooks/mirror-hook.py")
    settings = json.loads(content)
    assert "hooks" in settings
    assert "PreToolUse" in settings["hooks"]
    hook_cmd = settings["hooks"]["PreToolUse"][0]["hooks"][0]["command"]
    assert "mirror-hook.py" in hook_cmd
    assert "$schema" in settings  # New files get schema


def test_merge_hook_into_settings_existing(tmp_path):
    settings_path = tmp_path / "settings.json"
    existing = {
        "hooks": {
            "PreToolUse": [{
                "matcher": "Bash",
                "hooks": [{"type": "command", "command": "my-custom-hook.sh"}]
            }]
        }
    }
    settings_path.write_text(json.dumps(existing))

    content = merge_hook_into_settings(settings_path, "python3 ~/.claude/hooks/mirror-hook.py")
    settings = json.loads(content)

    # Should preserve existing hook
    assert len(settings["hooks"]["PreToolUse"]) == 2
    assert settings["hooks"]["PreToolUse"][0]["hooks"][0]["command"] == "my-custom-hook.sh"
    assert "mirror-hook.py" in settings["hooks"]["PreToolUse"][1]["hooks"][0]["command"]


def test_merge_hook_into_settings_idempotent(tmp_path):
    settings_path = tmp_path / "settings.json"
    # First merge
    content = merge_hook_into_settings(settings_path, "python3 ~/.claude/hooks/mirror-hook.py")
    settings_path.write_text(content)
    # Second merge — should not add duplicate
    content2 = merge_hook_into_settings(settings_path, "python3 ~/.claude/hooks/mirror-hook.py")
    settings = json.loads(content2)
    assert len(settings["hooks"]["PreToolUse"]) == 1


def test_generate_mirror_json():
    content = generate_mirror_json(
        "python", False,
        {"rules/bash-safety.md": "abc123"},
        {".claude/CLAUDE.md": "def456"},
        ["claude-md", "rules"],
    )
    data = json.loads(content)
    assert data["version"] == "0.3.0"
    assert data["project_type"] == "python"
    assert data["grip_global"] is False
    assert "rules/bash-safety.md" in data["user_file_hashes"]
    assert ".claude/CLAUDE.md" in data["project_file_hashes"]


def test_scaffold_standalone_dry_run(tmp_path):
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()
    (project_dir / "pyproject.toml").write_text("[project]\n")

    result = scaffold_project(
        project_dir, "python", standalone=True, dry_run=True,
        user_claude_dir=user_dir,
    )

    assert result["mode"] == "standalone"
    assert result["project_type"] == "python"

    user_files = result["user_files"]
    project_files = result["project_files"]

    # Project-level: CLAUDE.md and tracking
    assert ".claude/CLAUDE.md" in project_files
    assert ".claude/.tokenguard.json" in project_files

    # User-level: rules, hooks, settings, memory
    assert "rules/efficiency-rules.md" in user_files
    assert "rules/bash-safety.md" in user_files
    assert "rules/commit-standards.md" in user_files
    assert "hooks/mirror-hook.py" in user_files
    assert "settings.json" in user_files
    assert "memory/MEMORY.md" in user_files

    # Dry run should NOT write files
    assert not (project_dir / ".claude" / "CLAUDE.md").exists()
    assert not (user_dir / "rules" / "efficiency-rules.md").exists()


def test_scaffold_standalone_writes_files(tmp_path):
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()

    result = scaffold_project(
        project_dir, "python", standalone=True,
        user_claude_dir=user_dir,
    )

    # Project-level files
    assert (project_dir / ".claude" / "CLAUDE.md").exists()
    assert (project_dir / ".claude" / ".tokenguard.json").exists()

    # User-level files
    assert (user_dir / "rules" / "efficiency-rules.md").exists()
    assert (user_dir / "hooks" / "mirror-hook.py").exists()
    assert (user_dir / "settings.json").exists()
    assert (user_dir / "memory" / "MEMORY.md").exists()

    # Verify CLAUDE.md content
    claude_md = (project_dir / ".claude" / "CLAUDE.md").read_text()
    assert "Safety Gates" in claude_md
    assert "python" in claude_md.lower()


def test_scaffold_no_hooks(tmp_path):
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()

    result = scaffold_project(
        project_dir, "python", standalone=True, no_hooks=True,
        user_claude_dir=user_dir,
    )

    assert "hooks/mirror-hook.py" not in result["user_files"]
    assert "settings.json" not in result["user_files"]
    assert not (user_dir / "hooks").exists()


def test_scaffold_mirror_json_integrity(tmp_path):
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()

    scaffold_project(
        project_dir, "python", standalone=True,
        user_claude_dir=user_dir,
    )

    mirror = json.loads((project_dir / ".claude" / ".tokenguard.json").read_text())
    assert mirror["version"] == "0.3.0"
    assert mirror["project_type"] == "python"
    assert "claude-md" in mirror["installed_components"]
    assert "rules" in mirror["installed_components"]
    assert "hooks" in mirror["installed_components"]
    # User-level hashes
    for rel_path, hash_val in mirror["user_file_hashes"].items():
        assert len(hash_val) == 16
    # Project-level hashes
    for rel_path, hash_val in mirror["project_file_hashes"].items():
        assert len(hash_val) == 16


def test_scaffold_javascript(tmp_path):
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()

    scaffold_project(
        project_dir, "javascript", standalone=True,
        user_claude_dir=user_dir,
    )
    claude_md = (project_dir / ".claude" / "CLAUDE.md").read_text()
    assert "JavaScript" in claude_md or "javascript" in claude_md.lower()


def test_scaffold_generic(tmp_path):
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()

    scaffold_project(
        project_dir, "generic", standalone=True,
        user_claude_dir=user_dir,
    )
    claude_md = (project_dir / ".claude" / "CLAUDE.md").read_text()
    assert "generic" in claude_md.lower()


def test_scaffold_skips_existing_user_files(tmp_path):
    """User-level files that already exist should be skipped."""
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()

    # Pre-create a rule file
    rules_dir = user_dir / "rules"
    rules_dir.mkdir(parents=True)
    (rules_dir / "efficiency-rules.md").write_text("# My custom rules\n")

    result = scaffold_project(
        project_dir, "python", standalone=True,
        user_claude_dir=user_dir,
    )

    # Should be in skipped list
    assert "rules/efficiency-rules.md" in result["skipped_existing"]
    # Custom content preserved
    assert (rules_dir / "efficiency-rules.md").read_text() == "# My custom rules\n"


def test_scaffold_settings_merge_preserves_existing(tmp_path):
    """Settings.json should merge, not overwrite existing hooks."""
    project_dir = tmp_path / "project"
    project_dir.mkdir()
    user_dir = tmp_path / "user_claude"
    user_dir.mkdir()

    # Pre-create settings with an existing hook
    existing = {
        "hooks": {
            "PreToolUse": [{
                "matcher": "Bash",
                "hooks": [{"type": "command", "command": "my-lint.sh"}]
            }]
        }
    }
    (user_dir / "settings.json").write_text(json.dumps(existing))

    scaffold_project(
        project_dir, "python", standalone=True,
        user_claude_dir=user_dir,
    )

    settings = json.loads((user_dir / "settings.json").read_text())
    # Both hooks should be present
    assert len(settings["hooks"]["PreToolUse"]) == 2
    commands = [
        h["command"]
        for group in settings["hooks"]["PreToolUse"]
        for h in group["hooks"]
    ]
    assert "my-lint.sh" in commands
    assert any("mirror-hook.py" in c for c in commands)
