"""Integration tests for the init command with command processing."""

from unittest.mock import patch

import pytest
import yaml
from typer.testing import CliRunner

from quaestor.cli.main import app
from quaestor.constants import COMMAND_FILES


@pytest.fixture
def runner():
    """Create a CLI runner."""
    return CliRunner()


@pytest.fixture
def temp_git_project(tmp_path):
    """Create a temporary project with git initialized."""
    project_dir = tmp_path / "test_project"
    project_dir.mkdir()

    # Initialize git
    import subprocess

    subprocess.run(["git", "init"], cwd=project_dir, capture_output=True)

    return project_dir


@pytest.fixture
def project_with_config(temp_git_project):
    """Create a project with command configuration."""
    # Create .quaestor directory
    quaestor_dir = temp_git_project / ".quaestor"
    quaestor_dir.mkdir()

    # Create command config
    config_data = {
        "commands": {
            "task": {
                "enforcement": "strict",
                "parameters": {"minimum_test_coverage": 95},
                "custom_rules": ["All code must be reviewed", "No direct database access"],
            },
            "check": {"enforcement": "relaxed", "auto_fix": True},
        }
    }

    config_path = quaestor_dir / "command-config.yaml"
    with open(config_path, "w") as f:
        yaml.dump(config_data, f)

    # Create command override
    commands_dir = quaestor_dir / "commands"
    commands_dir.mkdir()
    (commands_dir / "status.md").write_text("# Custom Status Command\n\nThis is overridden.")

    return temp_git_project


class TestInitIntegration:
    """Test init command with command processing integration."""

    def test_personal_mode_init_basic(self, runner, temp_git_project):
        """Test basic personal mode initialization."""
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            result = runner.invoke(app, ["init", "--mode", "personal"])

        assert result.exit_code == 0
        assert "Personal mode initialization complete!" in result.output

        # Check directory structure
        assert (temp_git_project / ".claude").exists()
        assert (temp_git_project / ".quaestor").exists()
        assert (temp_git_project / "CLAUDE.md").exists()

        # Check settings.local.json
        settings_file = temp_git_project / ".claude" / "settings.local.json"
        assert settings_file.exists()

        # Check hooks installed to .quaestor/hooks
        hooks_dir = temp_git_project / ".quaestor" / "hooks"
        assert hooks_dir.exists()
        assert (hooks_dir / "workflow").exists()
        assert (hooks_dir / "validation").exists()

    def test_team_mode_init_basic(self, runner, temp_git_project):
        """Test basic team mode initialization."""
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            result = runner.invoke(app, ["init", "--mode", "team"])

        assert result.exit_code == 0
        assert "Team mode initialization complete!" in result.output

        # Check directory structure
        assert (temp_git_project / ".claude" / "commands").exists()
        assert (temp_git_project / ".quaestor").exists()
        assert (temp_git_project / "CLAUDE.md").exists()

        # Check settings.json (not .local)
        settings_file = temp_git_project / ".claude" / "settings.json"
        assert settings_file.exists()

        # Check manifest created
        manifest_file = temp_git_project / ".quaestor" / "manifest.json"
        assert manifest_file.exists()

    def test_init_with_existing_config(self, runner, project_with_config):
        """Test initialization with existing command configuration."""
        with patch("quaestor.cli.init.Path.cwd", return_value=project_with_config):
            result = runner.invoke(app, ["init", "--mode", "team", "--force"])

        assert result.exit_code == 0

        # Should show configured commands
        assert "(configured)" in result.output
        # Check that commands were installed with configuration
        assert "Installed task.md" in result.output or "Regenerated task.md" in result.output

        # Check task command has configuration applied
        task_file = project_with_config / ".claude" / "commands" / "task.md"
        assert task_file.exists()
        task_content = task_file.read_text()

        # Verify configuration was applied
        assert "<!-- CONFIGURED BY QUAESTOR" in task_content
        assert "PROJECT-SPECIFIC RULES" in task_content
        assert "All code must be reviewed" in task_content
        assert "No direct database access" in task_content
        # Parameters are not included in the output currently

        # Check status command has override (with configuration header)
        status_file = project_with_config / ".claude" / "commands" / "status.md"
        status_content = status_file.read_text()
        assert "<!-- CONFIGURED BY QUAESTOR" in status_content
        assert "Custom Status Command" in status_content
        assert "This is overridden" in status_content

        # Check relaxed command
        check_file = project_with_config / ".claude" / "commands" / "check.md"
        check_content = check_file.read_text()
        assert "relaxed enforcement" in check_content

    def test_personal_mode_commands_location(self, runner, temp_git_project):
        """Test personal mode installs commands to ~/.claude/commands."""
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            # Mock home directory
            mock_home = temp_git_project / "mock_home"
            mock_home.mkdir()
            mock_claude_dir = mock_home / ".claude"
            mock_commands_dir = mock_claude_dir / "commands"

            # Patch the DEFAULT_COMMANDS_DIR constant directly
            with patch("quaestor.cli.init.DEFAULT_COMMANDS_DIR", mock_commands_dir):
                result = runner.invoke(app, ["init", "--mode", "personal"])

            assert result.exit_code == 0
            assert "Installing to ~/.claude/commands (personal commands)" in result.output

            # Commands should be in mock home
            assert mock_commands_dir.exists()

            # Verify all commands installed
            for cmd_file in COMMAND_FILES:
                assert (mock_commands_dir / cmd_file).exists()

    def test_init_update_scenario(self, runner, temp_git_project):
        """Test init behavior when updating existing installation."""
        # First init
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            result1 = runner.invoke(app, ["init", "--mode", "team"])
        assert result1.exit_code == 0

        # Modify a file to simulate user changes
        memory_file = temp_git_project / ".quaestor" / "MEMORY.md"
        original_content = memory_file.read_text()
        memory_file.write_text(original_content + "\n## User Notes\nCustom content")

        # Second init should trigger update check
        with (
            patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project),
            patch("rich.prompt.Confirm.ask", return_value=False),
        ):
            result2 = runner.invoke(app, ["init", "--mode", "team"])

        assert "Checking for updates" in result2.output
        # Since no files changed, it should say everything is up to date
        assert "Everything is up to date" in result2.output

    def test_gitignore_behavior(self, runner, temp_git_project):
        """Test gitignore modifications for each mode."""
        # Test personal mode
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            runner.invoke(app, ["init", "--mode", "personal"])

        gitignore = temp_git_project / ".gitignore"
        assert gitignore.exists()
        gitignore_content = gitignore.read_text()

        # Personal mode ignores .quaestor and settings.local.json
        assert ".quaestor/" in gitignore_content
        assert ".claude/settings.local.json" in gitignore_content

        # Clean up
        gitignore.unlink()

        # Test team mode
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            runner.invoke(app, ["init", "--mode", "team", "--force"])

        # Team mode should not modify gitignore
        assert not gitignore.exists()

    def test_hook_installation(self, runner, temp_git_project):
        """Test hook files are properly installed."""
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            result = runner.invoke(app, ["init", "--mode", "team"])

        assert result.exit_code == 0
        assert "Installing hook files:" in result.output
        assert "Installed 16 hook files" in result.output

        # Check hook structure
        hooks_dir = temp_git_project / ".quaestor" / "hooks"
        assert (hooks_dir / "shared_utils.py").exists()
        assert (hooks_dir / "workflow" / "automated_commit_trigger.py").exists()
        assert (hooks_dir / "workflow" / "todo_milestone_connector.py").exists()
        assert (hooks_dir / "validation" / "compliance_validator.py").exists()
        assert (hooks_dir / "validation" / "research_enforcer.py").exists()

    def test_settings_json_paths(self, runner, temp_git_project):
        """Test settings.json has correct hook paths."""
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            runner.invoke(app, ["init", "--mode", "team"])

        settings_file = temp_git_project / ".claude" / "settings.json"
        settings_content = settings_file.read_text()

        # Should have workflow subdirectory in paths
        assert "workflow/implementation_declaration.py" in settings_content
        assert "workflow/research_tracker.py" in settings_content
        assert "workflow/memory_updater.py" in settings_content

        # Should not have old paths
        assert "hooks/implementation_declaration.py" not in settings_content
        assert "hooks/research_tracker.py" not in settings_content

    def test_error_handling(self, runner, temp_git_project):
        """Test error handling during initialization."""
        # Test with non-git directory
        non_git_dir = temp_git_project / "not_git"
        non_git_dir.mkdir()

        with patch("quaestor.cli.init.Path.cwd", return_value=non_git_dir):
            result = runner.invoke(app, ["init"])

        # Should still succeed but with appropriate handling
        assert result.exit_code == 0

        # Test with permission issues
        with patch("pathlib.Path.mkdir") as mock_mkdir:
            mock_mkdir.side_effect = PermissionError("No permission")

            with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
                result = runner.invoke(app, ["init", "--force"])

            # Should handle gracefully
            assert result.exit_code != 0 or "permission" in result.output.lower()


class TestCommandProcessingIntegration:
    """Test command processing during initialization."""

    def test_command_processor_integration(self, runner, project_with_config):
        """Test CommandProcessor is properly integrated."""
        with patch("importlib.resources.read_text") as mock_read:
            # Mock command content
            mock_read.return_value = """---
allowed-tools: [Read, Write]
---
# Task Command

minimum_test_coverage: 80
"""

            with patch("quaestor.cli.init.Path.cwd", return_value=project_with_config):
                result = runner.invoke(app, ["init", "--mode", "team", "--force"])

        assert result.exit_code == 0

        # Verify processor was used
        task_file = project_with_config / ".claude" / "commands" / "task.md"
        content = task_file.read_text()

        # Should have configuration marker
        assert "<!-- CONFIGURED BY QUAESTOR" in content
        assert "Base command: task" in content
        assert "Configuration: .quaestor/command-config.yaml" in content

    def test_manifest_tracking(self, runner, temp_git_project):
        """Test manifest properly tracks files in both modes."""
        # Test personal mode (should have manifest now)
        with patch("quaestor.cli.init.Path.cwd", return_value=temp_git_project):
            runner.invoke(app, ["init", "--mode", "personal"])

        manifest_file = temp_git_project / ".quaestor" / "manifest.json"
        assert manifest_file.exists()

        import json

        with open(manifest_file) as f:
            manifest = json.load(f)

        assert "version" in manifest
        assert "quaestor_version" in manifest
        assert "files" in manifest

        # Check tracked files
        tracked_files = manifest["files"]
        assert ".quaestor/QUAESTOR_CLAUDE.md" in tracked_files
        assert ".quaestor/MEMORY.md" in tracked_files
