"""
End-to-end tests for CLI workflows.

These tests verify complete CLI usage scenarios from command-line
arguments through analysis to output generation.
"""

import pytest
import tempfile
import os
import json
import sys
from hbat.cli.main import create_parser, load_parameters_from_args, load_preset_file
from hbat.core.analyzer import MolecularInteractionAnalyzer


@pytest.mark.e2e
@pytest.mark.requires_pdb_files
class TestCLIAnalysisWorkflows:
    """Test complete CLI analysis workflows."""

    def test_basic_cli_analysis_workflow(self, sample_pdb_file):
        """Test basic CLI analysis workflow: args → parameters → analysis → results."""
        parser = create_parser()

        # Simulate CLI arguments
        args = parser.parse_args(
            [sample_pdb_file, "--hb-distance", "3.5", "--hb-angle", "120"]
        )

        # Load parameters from CLI
        params = load_parameters_from_args(args)
        assert params.hb_distance_cutoff == 3.5
        assert params.hb_angle_cutoff == 120.0

        # Execute analysis
        analyzer = MolecularInteractionAnalyzer(params)
        success = analyzer.analyze_file(sample_pdb_file)
        assert success, "CLI-configured analysis should succeed"

        # Verify results
        # Create statistics from analyzer results
        stats = {
            "hydrogen_bonds": len(analyzer.hydrogen_bonds),
            "halogen_bonds": len(analyzer.halogen_bonds),
            "pi_interactions": len(analyzer.pi_interactions),
            "total_interactions": len(analyzer.hydrogen_bonds)
            + len(analyzer.halogen_bonds)
            + len(analyzer.pi_interactions),
        }
        assert stats["hydrogen_bonds"] > 0
        assert stats["total_interactions"] > 0

    def test_cli_output_format_workflow(self, sample_pdb_file):
        """Test CLI workflow with different output formats."""
        parser = create_parser()

        # Create temporary output files
        with (
            tempfile.NamedTemporaryFile(
                mode="w", suffix=".json", delete=False
            ) as json_file,
            tempfile.NamedTemporaryFile(
                mode="w", suffix=".csv", delete=False
            ) as csv_file,
        ):
            json_path = json_file.name
            csv_path = csv_file.name

        try:
            # Parse CLI arguments with output options
            args = parser.parse_args(
                [sample_pdb_file, "--json", json_path, "--csv", csv_path, "--verbose"]
            )

            # Load parameters and run analysis
            params = load_parameters_from_args(args)
            analyzer = MolecularInteractionAnalyzer(params)
            success = analyzer.analyze_file(sample_pdb_file)
            assert success

            # Simulate output generation (normally done by CLI main)
            # Create statistics from analyzer results
            stats = {
                "hydrogen_bonds": len(analyzer.hydrogen_bonds),
                "halogen_bonds": len(analyzer.halogen_bonds),
                "pi_interactions": len(analyzer.pi_interactions),
                "total_interactions": len(analyzer.hydrogen_bonds)
                + len(analyzer.halogen_bonds)
                + len(analyzer.pi_interactions),
            }

            # Generate JSON output
            json_output = {
                "file": sample_pdb_file,
                "statistics": stats,
                "parameters": {
                    "hb_distance_cutoff": params.hb_distance_cutoff,
                    "hb_angle_cutoff": params.hb_angle_cutoff,
                    "analysis_mode": params.analysis_mode,
                },
            }

            with open(json_path, "w") as f:
                json.dump(json_output, f, indent=2)

            # Verify output files
            assert os.path.exists(json_path)

            # Validate JSON content
            with open(json_path, "r") as f:
                loaded_data = json.load(f)

            assert "statistics" in loaded_data
            assert "parameters" in loaded_data
            assert loaded_data["statistics"]["hydrogen_bonds"] > 0

        finally:
            for path in [json_path, csv_path]:
                if os.path.exists(path):
                    os.unlink(path)

    def test_cli_pdb_fixing_workflow(self, pdb_fixing_test_file):
        """Test CLI workflow with PDB fixing enabled."""
        parser = create_parser()

        # Parse CLI arguments with PDB fixing
        args = parser.parse_args(
            [
                pdb_fixing_test_file,
                "--fix-pdb",
                "--fix-method",
                "openbabel",
                "--fix-add-hydrogens",
            ]
        )

        # Load parameters
        params = load_parameters_from_args(args)
        assert params.fix_pdb_enabled is True
        assert params.fix_pdb_method == "openbabel"
        assert params.fix_pdb_add_hydrogens is True

        # Execute analysis with PDB fixing
        analyzer = MolecularInteractionAnalyzer(params)
        success = analyzer.analyze_file(pdb_fixing_test_file)
        assert success, "CLI PDB fixing workflow should succeed"

        # Verify PDB fixing occurred
        # Create statistics from analyzer results
        stats = {
            "hydrogen_bonds": len(analyzer.hydrogen_bonds),
            "halogen_bonds": len(analyzer.halogen_bonds),
            "pi_interactions": len(analyzer.pi_interactions),
            "total_interactions": len(analyzer.hydrogen_bonds)
            + len(analyzer.halogen_bonds)
            + len(analyzer.pi_interactions),
        }
        assert (
            stats["total_interactions"] >= 0
        )  # Should have some interactions after fixing

    def test_cli_error_handling_workflow(self):
        """Test CLI error handling workflow."""
        parser = create_parser()

        # Test with invalid file
        args = parser.parse_args(["nonexistent_file.pdb"])
        params = load_parameters_from_args(args)
        analyzer = MolecularInteractionAnalyzer(params)

        success = analyzer.analyze_file("nonexistent_file.pdb")
        assert not success, "Should fail for non-existent file"

        # Test invalid parameter combinations
        args = parser.parse_args(
            [
                "test.pdb",
                "--hb-distance",
                "-1.0",  # Invalid negative distance
            ]
        )

        # Parameter loading should handle or validate this
        try:
            params = load_parameters_from_args(args)
            # If no validation in parameter loading, analyzer should handle it
            analyzer = MolecularInteractionAnalyzer(params)
            assert params.hb_distance_cutoff == -1.0  # Should accept the value as given
        except ValueError:
            # Acceptable if parameter validation catches this
            pass


@pytest.mark.e2e
class TestCLIPresetWorkflows:
    """Test CLI preset management workflows."""

    def test_preset_creation_and_usage_workflow(self, sample_pdb_file):
        """Test complete preset creation and usage workflow."""
        # Create a custom preset file
        preset_data = {
            "format_version": "1.0",
            "application": "HBAT",
            "description": "Test preset for workflow",
            "parameters": {
                "hydrogen_bonds": {
                    "h_a_distance_cutoff": 3.2,
                    "dha_angle_cutoff": 130.0,
                    "d_a_distance_cutoff": 4.0,
                },
                "halogen_bonds": {
                    "x_a_distance_cutoff": 4.0,
                    "dxa_angle_cutoff": 120.0,
                },
                "pi_interactions": {
                    "h_pi_distance_cutoff": 4.5,
                    "dh_pi_angle_cutoff": 90.0,
                },
                "general": {
                    "covalent_cutoff_factor": 0.85,
                    "analysis_mode": "complete",
                },
                "pdb_fixing": {
                    "enabled": False,
                    "method": "openbabel",
                    "add_hydrogens": True,
                    "add_heavy_atoms": False,
                    "replace_nonstandard": False,
                    "remove_heterogens": False,
                    "keep_water": True,
                },
            },
        }

        # Save preset to temporary file
        with tempfile.NamedTemporaryFile(mode="w", suffix=".hbat", delete=False) as f:
            json.dump(preset_data, f)
            preset_path = f.name

        try:
            # Test loading preset file
            params = load_preset_file(preset_path)
            assert params.hb_distance_cutoff == 3.2
            assert params.hb_angle_cutoff == 130.0
            assert params.analysis_mode == "complete"

            # Test using preset in analysis
            analyzer = MolecularInteractionAnalyzer(params)
            success = analyzer.analyze_file(sample_pdb_file)
            assert success, "Preset-based analysis should succeed"

            # Verify preset parameters were applied
            assert analyzer.parameters.hb_distance_cutoff == 3.2
            assert analyzer.parameters.hb_angle_cutoff == 130.0

        except SystemExit:
            # Acceptable if preset loading fails in test environment
            pytest.skip("Preset loading not available in test environment")
        finally:
            os.unlink(preset_path)

    def test_preset_override_workflow(self, sample_pdb_file):
        """Test workflow where CLI arguments override preset values."""
        # Create preset file
        preset_data = {
            "format_version": "1.0",
            "application": "HBAT",
            "description": "Base preset",
            "parameters": {
                "hydrogen_bonds": {
                    "h_a_distance_cutoff": 3.0,
                    "dha_angle_cutoff": 120.0,
                    "d_a_distance_cutoff": 4.0,
                },
                "halogen_bonds": {
                    "x_a_distance_cutoff": 4.0,
                    "dxa_angle_cutoff": 120.0,
                },
                "pi_interactions": {
                    "h_pi_distance_cutoff": 4.5,
                    "dh_pi_angle_cutoff": 90.0,
                },
                "general": {
                    "covalent_cutoff_factor": 0.85,
                    "analysis_mode": "complete",
                },
                "pdb_fixing": {
                    "enabled": False,
                    "method": "openbabel",
                    "add_hydrogens": True,
                    "add_heavy_atoms": False,
                    "replace_nonstandard": False,
                    "remove_heterogens": False,
                    "keep_water": True,
                },
            },
        }

        with tempfile.NamedTemporaryFile(mode="w", suffix=".hbat", delete=False) as f:
            json.dump(preset_data, f)
            preset_path = f.name

        try:
            # Simulate CLI with preset and override
            parser = create_parser()

            # First load preset parameters
            preset_params = load_preset_file(preset_path)
            assert preset_params.hb_distance_cutoff == 3.0

            # Then test override logic (simulated since the actual CLI handles this)
            # In real CLI, this would be: ["file.pdb", "--preset", "name", "--hb-distance", "3.5"]
            override_args = parser.parse_args(
                [
                    sample_pdb_file,
                    "--hb-distance",
                    "3.5",  # Override preset value
                ]
            )

            override_params = load_parameters_from_args(override_args)
            assert (
                override_params.hb_distance_cutoff == 3.5
            )  # Override should take effect

        except SystemExit:
            pytest.skip("Preset functionality not available in test environment")
        finally:
            os.unlink(preset_path)


@pytest.mark.e2e
@pytest.mark.requires_pdb_files
class TestCLIValidationWorkflows:
    """Test CLI input validation workflows."""

    def test_parameter_validation_workflow(self, sample_pdb_file):
        """Test CLI parameter validation workflow."""
        parser = create_parser()

        # Test various parameter combinations
        test_cases = [
            # Valid parameters
            ([sample_pdb_file, "--hb-distance", "3.5"], True),
            ([sample_pdb_file, "--hb-angle", "120"], True),
            ([sample_pdb_file, "--mode", "complete"], True),
            ([sample_pdb_file, "--mode", "local"], True),
            # Edge case parameters (should be handled gracefully)
            (
                [sample_pdb_file, "--hb-distance", "0.8"],
                True,
            ),  # Small distance within valid range
            ([sample_pdb_file, "--hb-angle", "180"], True),  # Maximum angle
        ]

        for args_list, should_succeed in test_cases:
            args = parser.parse_args(args_list)
            params = load_parameters_from_args(args)

            # Parameters should load successfully
            assert isinstance(params, type(params))

            # Analysis may or may not succeed depending on parameter values
            analyzer = MolecularInteractionAnalyzer(params)
            success = analyzer.analyze_file(sample_pdb_file)

            if should_succeed:
                # For valid parameters, analysis should generally work
                # (though it might find no interactions with extreme parameters)
                assert isinstance(success, bool)

    def test_file_validation_workflow(self):
        """Test file validation workflow."""
        parser = create_parser()

        # Test with various file scenarios
        test_files = [
            ("nonexistent.pdb", False),
            ("", False),  # Empty filename
        ]

        for filename, should_succeed in test_files:
            if filename:  # Skip empty filename test for argument parsing
                args = parser.parse_args([filename])
                params = load_parameters_from_args(args)
                analyzer = MolecularInteractionAnalyzer(params)

                success = analyzer.analyze_file(filename)
                assert success == should_succeed

    def test_help_and_info_workflows(self):
        """Test CLI help and information workflows."""
        parser = create_parser()

        # Test that help can be generated
        help_text = parser.format_help()
        assert len(help_text) > 0
        assert "HBAT" in help_text
        assert "--hb-distance" in help_text

        # Test version information (if available)
        try:
            from hbat import __version__

            assert len(__version__) > 0
        except ImportError:
            # Version info not available, which is acceptable
            pass


@pytest.mark.e2e
@pytest.mark.requires_pdb_files
@pytest.mark.slow
class TestCLIPerformanceWorkflows:
    """Test CLI performance workflows."""

    def test_cli_performance_workflow(self, sample_pdb_file):
        """Test CLI performance with timing measurements."""
        parser = create_parser()

        # Configure for performance testing
        args = parser.parse_args(
            [
                sample_pdb_file,
                "--mode",
                "complete",  # Most comprehensive analysis
            ]
        )

        params = load_parameters_from_args(args)
        analyzer = MolecularInteractionAnalyzer(params)

        import time

        start_time = time.time()

        success = analyzer.analyze_file(sample_pdb_file)

        analysis_time = time.time() - start_time

        assert success, "Performance test analysis should succeed"
        assert analysis_time < 60.0, f"CLI analysis took too long: {analysis_time:.2f}s"

        # Verify substantial results
        # Create statistics from analyzer results
        stats = {
            "hydrogen_bonds": len(analyzer.hydrogen_bonds),
            "halogen_bonds": len(analyzer.halogen_bonds),
            "pi_interactions": len(analyzer.pi_interactions),
            "total_interactions": len(analyzer.hydrogen_bonds)
            + len(analyzer.halogen_bonds)
            + len(analyzer.pi_interactions),
        }
        assert stats["total_interactions"] > 0

    def test_memory_usage_workflow(self, sample_pdb_file):
        """Test CLI memory usage workflow."""
        parser = create_parser()
        args = parser.parse_args([sample_pdb_file])

        params = load_parameters_from_args(args)
        analyzer = MolecularInteractionAnalyzer(params)

        # Monitor basic memory usage

        initial_objects = len(sys.modules)

        success = analyzer.analyze_file(sample_pdb_file)
        assert success

        # Check that we haven't leaked too many modules
        final_objects = len(sys.modules)
        module_growth = final_objects - initial_objects

        # Some module growth is expected, but not excessive
        assert module_growth < 50, f"Too many new modules loaded: {module_growth}"
