"""
Tests for Auto-Configuration CLI Command
=========================================

WHY: Comprehensive tests for the auto-configure command to ensure reliable
automated agent configuration with proper error handling and user feedback.

Part of TSK-0054: Auto-Configuration Feature - Phase 5
"""

import json
from argparse import Namespace
from pathlib import Path
from unittest.mock import MagicMock, Mock, patch

import pytest

from claude_mpm.cli.commands.auto_configure import AutoConfigureCommand
from claude_mpm.cli.shared import CommandResult
from claude_mpm.services.core.models.agent_config import (
    AgentRecommendation,
    ConfigurationPreview,
    ConfigurationResult,
    ConfigurationStatus,
    ValidationResult,
)
from claude_mpm.services.core.models.toolchain import ToolchainAnalysis


class TestAutoConfigureCommand:
    """Test suite for AutoConfigureCommand."""

    @pytest.fixture
    def command(self):
        """Create command instance."""
        return AutoConfigureCommand()

    @pytest.fixture
    def mock_auto_config_manager(self):
        """Create mock auto-config manager."""
        manager = Mock()
        manager.preview_configuration = Mock()
        manager.execute_configuration = Mock()
        return manager

    @pytest.fixture
    def sample_toolchain_analysis(self):
        """Create sample toolchain analysis."""
        analysis = Mock(spec=ToolchainAnalysis)
        analysis.components = []
        analysis.languages = []
        analysis.frameworks = []
        analysis.deployment_targets = []
        return analysis

    @pytest.fixture
    def sample_preview(self, sample_toolchain_analysis):
        """Create sample configuration preview."""
        preview = Mock(spec=ConfigurationPreview)
        preview.detected_toolchain = sample_toolchain_analysis
        preview.recommendations = [
            Mock(
                spec=AgentRecommendation,
                agent_id="python-engineer",
                confidence=0.95,
                reasoning="Python detected",
                matched_capabilities=[],
            )
        ]
        preview.validation_result = Mock(
            spec=ValidationResult, is_valid=True, issues=[]
        )
        return preview

    @pytest.fixture
    def sample_result(self):
        """Create sample configuration result."""
        result = Mock(spec=ConfigurationResult)
        result.status = ConfigurationStatus.SUCCESS
        result.deployed_agents = ["python-engineer"]
        result.failed_agents = []
        result.errors = {}
        return result

    def test_validate_args_valid(self, command):
        """Test argument validation with valid arguments."""
        args = Namespace(
            project_path=Path.cwd(), min_confidence=0.8, preview=False, yes=False
        )

        error = command.validate_args(args)
        assert error is None

    def test_validate_args_invalid_path(self, command):
        """Test argument validation with invalid path."""
        args = Namespace(
            project_path=Path("/nonexistent/path"),
            min_confidence=0.8,
            preview=False,
            yes=False,
        )

        error = command.validate_args(args)
        assert error is not None
        assert "does not exist" in error

    def test_validate_args_invalid_confidence(self, command):
        """Test argument validation with invalid confidence."""
        args = Namespace(
            project_path=Path.cwd(), min_confidence=1.5, preview=False, yes=False
        )

        error = command.validate_args(args)
        assert error is not None
        assert "between 0.0 and 1.0" in error

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_run_preview_mode(
        self, mock_service_class, command, sample_preview, mock_auto_config_manager
    ):
        """Test running in preview mode."""
        mock_auto_config_manager.preview_configuration.return_value = sample_preview
        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        args = Namespace(
            project_path=Path.cwd(),
            min_confidence=0.8,
            preview=True,
            dry_run=False,
            yes=False,
            json=False,
            verbose=False,
            debug=False,
            quiet=False,
        )

        result = command.run(args)

        assert isinstance(result, CommandResult)
        assert result.success
        mock_auto_config_manager.preview_configuration.assert_called_once()

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_run_preview_json_output(
        self, mock_service_class, command, sample_preview, mock_auto_config_manager
    ):
        """Test preview mode with JSON output."""
        mock_auto_config_manager.preview_configuration.return_value = sample_preview
        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        args = Namespace(
            project_path=Path.cwd(),
            min_confidence=0.8,
            preview=True,
            dry_run=False,
            yes=False,
            json=True,
            verbose=False,
            debug=False,
            quiet=False,
        )

        with patch("builtins.print") as mock_print:
            result = command.run(args)

            assert result.success
            # Verify JSON output was printed
            mock_print.assert_called_once()
            output = mock_print.call_args[0][0]
            # Should be valid JSON
            json.loads(output)

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_run_full_with_skip_confirmation(
        self,
        mock_service_class,
        command,
        sample_preview,
        sample_result,
        mock_auto_config_manager,
    ):
        """Test full configuration with confirmation skipped."""
        mock_auto_config_manager.preview_configuration.return_value = sample_preview
        mock_auto_config_manager.execute_configuration.return_value = sample_result
        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        args = Namespace(
            project_path=Path.cwd(),
            min_confidence=0.8,
            preview=False,
            dry_run=False,
            yes=True,
            json=False,
            verbose=False,
            debug=False,
            quiet=False,
        )

        result = command.run(args)

        assert result.success
        mock_auto_config_manager.execute_configuration.assert_called_once()

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_run_keyboard_interrupt(
        self, mock_service_class, command, mock_auto_config_manager
    ):
        """Test handling of keyboard interrupt."""
        mock_auto_config_manager.preview_configuration.side_effect = KeyboardInterrupt()
        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        args = Namespace(
            project_path=Path.cwd(),
            min_confidence=0.8,
            preview=True,
            dry_run=False,
            yes=False,
            json=False,
            verbose=False,
            debug=False,
            quiet=False,
        )

        result = command.run(args)

        assert not result.success
        assert result.exit_code == 130

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_run_exception_handling(
        self, mock_service_class, command, mock_auto_config_manager
    ):
        """Test exception handling."""
        mock_auto_config_manager.preview_configuration.side_effect = Exception(
            "Test error"
        )
        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        args = Namespace(
            project_path=Path.cwd(),
            min_confidence=0.8,
            preview=True,
            dry_run=False,
            yes=False,
            json=False,
            verbose=False,
            debug=False,
            quiet=False,
        )

        result = command.run(args)

        assert not result.success
        assert "Test error" in result.message

    def test_confirm_deployment_yes(self, command, sample_preview):
        """Test deployment confirmation with yes response."""
        with patch("builtins.input", return_value="y"):
            result = command._confirm_deployment(sample_preview)
            assert result is True

    def test_confirm_deployment_no(self, command, sample_preview):
        """Test deployment confirmation with no response."""
        with patch("builtins.input", return_value="n"):
            result = command._confirm_deployment(sample_preview)
            assert result is False

    def test_confirm_deployment_empty_recommendations(self, command, sample_preview):
        """Test deployment confirmation with no recommendations."""
        sample_preview.recommendations = []
        result = command._confirm_deployment(sample_preview)
        assert result is False

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_display_result_success(
        self, mock_service_class, command, sample_result, mock_auto_config_manager
    ):
        """Test display of successful result."""
        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        result = command._display_result(sample_result)

        assert result.success

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_display_result_partial(
        self, mock_service_class, command, sample_result, mock_auto_config_manager
    ):
        """Test display of partial result."""
        sample_result.status = ConfigurationStatus.PARTIAL_SUCCESS
        sample_result.failed_agents = ["ops"]
        sample_result.errors = {"ops": "Deployment failed"}

        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        result = command._display_result(sample_result)

        assert not result.success
        assert result.exit_code == 1

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_display_result_failure(
        self, mock_service_class, command, sample_result, mock_auto_config_manager
    ):
        """Test display of failed result."""
        sample_result.status = ConfigurationStatus.FAILURE
        sample_result.deployed_agents = []
        sample_result.errors = {"general": "Configuration failed"}

        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        result = command._display_result(sample_result)

        assert not result.success
        assert result.exit_code == 1

    @patch("claude_mpm.cli.commands.auto_configure.AutoConfigManagerService")
    def test_output_result_json(
        self, mock_service_class, command, sample_result, mock_auto_config_manager
    ):
        """Test JSON output for result."""
        mock_service_class.return_value = mock_auto_config_manager
        command._auto_config_manager = mock_auto_config_manager

        with patch("builtins.print") as mock_print:
            result = command._output_result_json(sample_result)

            assert result.success
            mock_print.assert_called_once()
            output = mock_print.call_args[0][0]
            data = json.loads(output)
            assert "status" in data
            assert "deployed_agents" in data
