"""
Unit tests for settings merger with wildcard subsumption.
"""

import sys
from pathlib import Path

# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))

from persuade.settings_merger import SettingsMerger


def test_parse_permission() -> None:
    """Test permission parsing into structured format."""
    merger = SettingsMerger()

    # Simple tool
    assert merger._parse_permission("Read") == {"tool": "Read", "args": None}

    # Tool with args
    assert merger._parse_permission("Bash(git status)") == {
        "tool": "Bash",
        "args": ["git", "status"],
    }

    # Tool with wildcard
    assert merger._parse_permission("Bash(git :*)") == {"tool": "Bash", "args": ["git", ":*"]}

    print("✓ test_parse_permission passed")


def test_wildcard_matches() -> None:
    """Test wildcard pattern matching."""
    merger = SettingsMerger()

    # Exact wildcard match
    assert merger._wildcard_matches(["git", ":*"], ["git", "status"]) == True
    assert merger._wildcard_matches(["git", ":*"], ["git", "log", "--oneline"]) == True

    # Prefix mismatch
    assert merger._wildcard_matches(["git", ":*"], ["npm", "test"]) == False

    # Complex pattern
    assert merger._wildcard_matches(["gh", "pr", ":*"], ["gh", "pr", "view", "123"]) == True
    assert merger._wildcard_matches(["gh", "pr", ":*"], ["gh", "issue", "list"]) == False

    # No wildcard - exact match only
    assert merger._wildcard_matches(["git", "status"], ["git", "status"]) == True
    assert merger._wildcard_matches(["git", "status"], ["git", "log"]) == False

    print("✓ test_wildcard_matches passed")


def test_subsumption() -> None:
    """Test that wildcard patterns subsume specific permissions."""
    merger = SettingsMerger()

    # git wildcard subsumes specific git commands
    git_wildcard = merger._parse_permission("Bash(git :*)")
    git_status = merger._parse_permission("Bash(git status)")
    git_log = merger._parse_permission("Bash(git log)")

    assert merger._is_subsumed(git_status, [git_wildcard]) == True
    assert merger._is_subsumed(git_log, [git_wildcard]) == True

    # npm command not subsumed by git wildcard
    npm_test = merger._parse_permission("Bash(npm test)")
    assert merger._is_subsumed(npm_test, [git_wildcard]) == False

    # Exact match is subsumption
    assert merger._is_subsumed(git_status, [git_status]) == True

    print("✓ test_subsumption passed")


def test_union_merge() -> None:
    """Test union merge deduplicates and preserves unique permissions."""
    merger = SettingsMerger()

    source = {"permissions": {"allow": ["Read", "Grep", "Bash(git status)"]}}

    target = {"permissions": {"allow": ["Read", "Bash(npm test)"]}}

    result = merger.merge(source, target, mode="union")

    allow = result["permissions"]["allow"]

    # Read should appear once (deduplicated)
    assert allow.count("Read") == 1

    # Grep from source should be present
    assert "Grep" in allow

    # Bash(git status) from source should be present
    assert "Bash(git status)" in allow

    # Bash(npm test) from target should be preserved
    assert "Bash(npm test)" in allow

    # Total should be 4
    assert len(allow) == 4

    print("✓ test_union_merge passed")


def test_wildcard_deduplication() -> None:
    """Test that wildcard patterns eliminate subsumed permissions."""
    merger = SettingsMerger()

    source = {"permissions": {"allow": ["Bash(git :*)"]}}

    target = {"permissions": {"allow": ["Bash(git status)", "Bash(git log)", "Bash(npm test)"]}}

    result = merger.merge(source, target, mode="union")

    allow = result["permissions"]["allow"]

    # git wildcard should be present
    assert "Bash(git :*)" in allow

    # git status and git log should NOT be present (subsumed)
    assert "Bash(git status)" not in allow
    assert "Bash(git log)" not in allow

    # npm test should be present (not subsumed by git wildcard)
    assert "Bash(npm test)" in allow

    print("✓ test_wildcard_deduplication passed")


def test_deny_list_preserved() -> None:
    """Test that target deny list is never modified."""
    merger = SettingsMerger()

    source = {"permissions": {"deny": ["Bash(rm -rf :*)"]}}

    target = {"permissions": {"deny": ["Bash(rm -rf :*)", "Read(.env)"]}}

    result = merger.merge(source, target, mode="union")

    # Target deny list preserved exactly
    assert result["permissions"]["deny"] == ["Bash(rm -rf :*)", "Read(.env)"]

    print("✓ test_deny_list_preserved passed")


def test_strict_mode() -> None:
    """Test that strict mode removes custom permissions."""
    merger = SettingsMerger()

    source = {"permissions": {"allow": ["Read", "Grep"], "deny": ["Bash(rm :*)"]}}

    target = {
        "permissions": {"allow": ["Read", "Bash(npm test)"], "deny": ["Bash(rm :*)", "Read(.env)"]}
    }

    result = merger.merge(source, target, mode="strict")

    allow = result["permissions"]["allow"]

    # Only source permissions
    assert "Read" in allow
    assert "Grep" in allow
    assert "Bash(npm test)" not in allow  # Custom perm removed

    # Deny list still preserved
    assert result["permissions"]["deny"] == ["Bash(rm :*)", "Read(.env)"]

    print("✓ test_strict_mode passed")


def test_hooks_merge() -> None:
    """Test that hooks are merged by event and matcher."""
    merger = SettingsMerger()

    source = {
        "hooks": {
            "PostToolUse": [
                {
                    "matcher": "Bash(gh pr create :*)",
                    "hooks": [{"type": "command", "command": "hook1"}],
                }
            ]
        }
    }

    target = {
        "hooks": {
            "PostToolUse": [
                {"matcher": "Bash(npm test)", "hooks": [{"type": "command", "command": "hook2"}]}
            ]
        }
    }

    result = merger.merge(source, target, mode="union")

    post_use = result["hooks"]["PostToolUse"]

    # Both hooks should be present
    assert len(post_use) == 2

    # Find by matcher
    gh_hook = next((h for h in post_use if h["matcher"] == "Bash(gh pr create :*)"), None)
    npm_hook = next((h for h in post_use if h["matcher"] == "Bash(npm test)"), None)

    assert gh_hook is not None
    assert npm_hook is not None

    print("✓ test_hooks_merge passed")


def test_statusline_sync_union() -> None:
    """Test that statusLine config is synced from source to target in union mode."""
    merger = SettingsMerger()

    source = {
        "statusLine": {"type": "command", "command": "./statusline.sh"},
        "permissions": {"allow": ["Read"]},
    }

    target = {
        "permissions": {"allow": ["Read", "Grep"]},
    }

    result = merger.merge(source, target, mode="union")

    # statusLine from source should be in result
    assert "statusLine" in result
    assert result["statusLine"] == {"type": "command", "command": "./statusline.sh"}

    # permissions should still be merged correctly
    assert "Read" in result["permissions"]["allow"]
    assert "Grep" in result["permissions"]["allow"]

    print("✓ test_statusline_sync_union passed")


def test_statusline_sync_strict() -> None:
    """Test that statusLine config is synced from source to target in strict mode."""
    merger = SettingsMerger()

    source = {
        "statusLine": {"type": "command", "command": "./statusline.sh"},
        "permissions": {"allow": ["Read"]},
    }

    target = {
        "permissions": {"allow": ["Read", "Grep"]},
    }

    result = merger.merge(source, target, mode="strict")

    # statusLine from source should be in result
    assert "statusLine" in result
    assert result["statusLine"] == {"type": "command", "command": "./statusline.sh"}

    # In strict mode, only source permissions
    assert result["permissions"]["allow"] == ["Read"]

    print("✓ test_statusline_sync_strict passed")


def test_all_config_keys_synced() -> None:
    """Test that all top-level config keys are synced."""
    merger = SettingsMerger()

    source = {
        "companyAnnouncements": ["Announcement 1", "Announcement 2"],
        "env": {"VAR1": "value1"},
        "includeCoAuthoredBy": True,
        "spinnerTipsEnabled": True,
        "statusLine": {"type": "command", "command": "./statusline.sh"},
        "permissions": {"allow": ["Read"]},
        "hooks": {},
    }

    target = {
        "permissions": {"allow": ["Grep"]},
    }

    result = merger.merge(source, target, mode="union")

    # All source config keys should be present
    assert "companyAnnouncements" in result
    assert result["companyAnnouncements"] == ["Announcement 1", "Announcement 2"]
    assert "env" in result
    assert result["env"] == {"VAR1": "value1"}
    assert "includeCoAuthoredBy" in result
    assert result["includeCoAuthoredBy"] is True
    assert "spinnerTipsEnabled" in result
    assert result["spinnerTipsEnabled"] is True
    assert "statusLine" in result
    assert result["statusLine"] == {"type": "command", "command": "./statusline.sh"}

    # Permissions should still be merged
    assert "Read" in result["permissions"]["allow"]
    assert "Grep" in result["permissions"]["allow"]

    print("✓ test_all_config_keys_synced passed")


def test_target_only_keys_preserved() -> None:
    """Test that target-only config keys are preserved in union mode."""
    merger = SettingsMerger()

    source = {
        "statusLine": {"type": "command", "command": "./statusline.sh"},
        "permissions": {"allow": ["Read"]},
    }

    target = {
        "customTargetKey": {"some": "config"},
        "permissions": {"allow": ["Grep"]},
    }

    result = merger.merge(source, target, mode="union")

    # Source statusLine should be present
    assert "statusLine" in result
    assert result["statusLine"] == {"type": "command", "command": "./statusline.sh"}

    # Target-only key should be preserved
    assert "customTargetKey" in result
    assert result["customTargetKey"] == {"some": "config"}

    print("✓ test_target_only_keys_preserved passed")


def test_source_precedence_for_config() -> None:
    """Test that source takes precedence for non-permissions/hooks config."""
    merger = SettingsMerger()

    source = {
        "statusLine": {"type": "command", "command": "./source.sh"},
        "env": {"VAR": "source_value"},
        "permissions": {"allow": ["Read"]},
    }

    target = {
        "statusLine": {"type": "command", "command": "./target.sh"},
        "env": {"VAR": "target_value"},
        "permissions": {"allow": ["Grep"]},
    }

    result = merger.merge(source, target, mode="union")

    # Source config should win
    assert result["statusLine"] == {"type": "command", "command": "./source.sh"}
    assert result["env"] == {"VAR": "source_value"}

    # Permissions should still be merged (union mode)
    assert "Read" in result["permissions"]["allow"]
    assert "Grep" in result["permissions"]["allow"]

    print("✓ test_source_precedence_for_config passed")


def run_all_tests() -> None:
    """Run all tests."""
    print("\nRunning settings merger tests...\n")

    test_parse_permission()
    test_wildcard_matches()
    test_subsumption()
    test_union_merge()
    test_wildcard_deduplication()
    test_deny_list_preserved()
    test_strict_mode()
    test_hooks_merge()
    test_statusline_sync_union()
    test_statusline_sync_strict()
    test_all_config_keys_synced()
    test_target_only_keys_preserved()
    test_source_precedence_for_config()

    print("\n✅ All tests passed!\n")


if __name__ == "__main__":
    run_all_tests()
