"""
Tests for CLI command implementations.

Tests the basic functionality of all CLI commands to ensure
they parse arguments correctly and handle basic error cases.
"""

import pytest
import tempfile
import shutil
from pathlib import Path
from unittest.mock import patch, MagicMock

from precipgen.cli.main import main, create_parser
from precipgen.cli.commands import (
    discover_command,
    analyze_command,
    simulate_command,
    validate_command,
    station_info_command,
    list_outputs_command,
    clean_command
)


class TestCLIParser:
    """Test CLI argument parsing."""
    
    def test_create_parser(self):
        """Test that parser is created successfully."""
        parser = create_parser()
        assert parser is not None
        assert parser.prog == "precipgen"
    
    def test_help_output(self):
        """Test that help is displayed correctly."""
        # Help output doesn't raise SystemExit in our implementation
        result = main(["--help"])
        assert result == 0
    
    def test_version_output(self):
        """Test version output."""
        # Version output doesn't raise SystemExit in our implementation
        result = main(["--version"])
        assert result == 0
    
    def test_no_command(self):
        """Test behavior when no command is provided."""
        result = main([])
        assert result == 1


class TestDiscoverCommand:
    """Test discover command functionality."""
    
    def test_add_parser(self):
        """Test that discover parser is added correctly."""
        import argparse
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers()
        
        discover_parser = discover_command.add_parser(subparsers)
        assert discover_parser is not None
    
    def test_coordinate_validation(self):
        """Test coordinate validation function."""
        # Valid coordinates
        valid, error = discover_command.validate_coordinates(40.7, -111.9)
        assert valid is True
        assert error is None
        
        # Invalid latitude
        valid, error = discover_command.validate_coordinates(100.0, -111.9)
        assert valid is False
        assert "latitude" in error.lower()
        
        # Invalid longitude
        valid, error = discover_command.validate_coordinates(40.7, -200.0)
        assert valid is False
        assert "longitude" in error.lower()
    
    def test_geocode_location(self):
        """Test location geocoding."""
        # Known location
        coords, error = discover_command.geocode_location("Salt Lake City, UT")
        assert coords is not None
        assert error is None
        assert len(coords) == 2
        
        # Unknown location
        coords, error = discover_command.geocode_location("Nonexistent City, XX")
        assert coords is None
        assert error is not None
        assert "not found" in error.lower()
    
    @patch('precipgen.cli.commands.discover_command.find_nearby_stations')
    def test_execute_with_coordinates(self, mock_find_stations):
        """Test discover command execution with coordinates."""
        # Mock station data
        mock_station = MagicMock()
        mock_station.station_id = "USC00426128"
        mock_station.name = "Salt Lake City"
        mock_station.distance_km = 5.0
        mock_station.data_years = 50
        mock_station.quality_score = 0.95
        
        mock_find_stations.return_value = [mock_station]
        
        # Create mock args
        args = MagicMock()
        args.location = None
        args.coordinates = None
        args.lat = 40.7
        args.lon = -111.9
        args.radius = 50.0
        args.interactive = False
        args.max_results = 10
        args.output_dir = None
        
        result = discover_command.execute(args)
        assert result == 0
        mock_find_stations.assert_called_once()


class TestAnalyzeCommand:
    """Test analyze command functionality."""
    
    def test_add_parser(self):
        """Test that analyze parser is added correctly."""
        import argparse
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers()
        
        analyze_parser = analyze_command.add_parser(subparsers)
        assert analyze_parser is not None
    
    def test_load_precipitation_data_error(self):
        """Test error handling for missing data file."""
        with pytest.raises(Exception):  # DataLoadError
            analyze_command.load_precipitation_data("nonexistent_file.csv")
    
    def test_auto_detect_data_file(self):
        """Test auto-detection of data files."""
        # Should return None for non-existent station
        result = analyze_command.auto_detect_data_file("NONEXISTENT")
        assert result is None


class TestSimulateCommand:
    """Test simulate command functionality."""
    
    def test_add_parser(self):
        """Test that simulate parser is added correctly."""
        import argparse
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers()
        
        simulate_parser = simulate_command.add_parser(subparsers)
        assert simulate_parser is not None
    
    def test_load_parameter_manifest_error(self):
        """Test error handling for missing parameter file."""
        with pytest.raises(Exception):  # DataLoadError
            simulate_command.load_parameter_manifest("nonexistent_params.json")
    
    def test_generate_synthetic_precipitation(self):
        """Test synthetic precipitation generation."""
        # Create mock parameters
        params = {
            'monthly_parameters': {}
        }
        
        for month in range(1, 13):
            params['monthly_parameters'][str(month)] = {
                'p_ww': 0.7,
                'p_wd': 0.3,
                'alpha': 2.0,
                'beta': 1.5
            }
        
        # Generate short time series
        precip_data = simulate_command.generate_synthetic_precipitation(
            params, n_days=30, seed=42
        )
        
        assert len(precip_data) == 30
        assert precip_data.name == 'precipitation'
        assert all(precip_data >= 0)  # No negative precipitation


class TestValidateCommand:
    """Test validate command functionality."""
    
    def test_add_parser(self):
        """Test that validate parser is added correctly."""
        import argparse
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers()
        
        validate_parser = validate_command.add_parser(subparsers)
        assert validate_parser is not None
    
    def test_auto_detect_data_files(self):
        """Test auto-detection of data files."""
        # Should return None for non-existent station
        synthetic_path, historical_path = validate_command.auto_detect_data_files("NONEXISTENT")
        assert synthetic_path is None
        assert historical_path is None


class TestStationInfoCommand:
    """Test station-info command functionality."""
    
    def test_add_parser(self):
        """Test that station-info parser is added correctly."""
        import argparse
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers()
        
        station_info_parser = station_info_command.add_parser(subparsers)
        assert station_info_parser is not None
    
    def test_format_file_size(self):
        """Test file size formatting."""
        assert "B" in station_info_command.format_file_size(500)
        assert "KB" in station_info_command.format_file_size(1500)
        assert "MB" in station_info_command.format_file_size(1500000)


class TestListOutputsCommand:
    """Test list-outputs command functionality."""
    
    def test_add_parser(self):
        """Test that list-outputs parser is added correctly."""
        import argparse
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers()
        
        list_outputs_parser = list_outputs_command.add_parser(subparsers)
        assert list_outputs_parser is not None
    
    def test_format_file_size(self):
        """Test file size formatting."""
        assert "KB" in list_outputs_command.format_file_size(0.5)
        assert "MB" in list_outputs_command.format_file_size(1.5)
        assert "GB" in list_outputs_command.format_file_size(1500)


class TestCleanCommand:
    """Test clean command functionality."""
    
    def test_add_parser(self):
        """Test that clean parser is added correctly."""
        import argparse
        parser = argparse.ArgumentParser()
        subparsers = parser.add_subparsers()
        
        clean_parser = clean_command.add_parser(subparsers)
        assert clean_parser is not None
    
    def test_format_file_size(self):
        """Test file size formatting."""
        assert "KB" in clean_command.format_file_size(0.5)
        assert "MB" in clean_command.format_file_size(1.5)
        assert "GB" in clean_command.format_file_size(1500)
    
    def test_confirm_deletion(self):
        """Test deletion confirmation."""
        # Force mode should always return True
        assert clean_command.confirm_deletion("Test message", force=True) is True


class TestCLIIntegration:
    """Test CLI integration and error handling."""
    
    def test_main_with_invalid_command(self):
        """Test main function with invalid command."""
        result = main(["invalid-command"])
        assert result == 2  # argparse returns 2 for invalid arguments
    
    def test_main_with_discover_missing_args(self):
        """Test discover command with missing required arguments."""
        result = main(["discover"])
        assert result != 0  # Should fail due to missing location/coordinates
    
    def test_main_with_analyze_missing_args(self):
        """Test analyze command with missing required arguments."""
        result = main(["analyze"])
        assert result != 0  # Should fail due to missing required arguments
    
    def test_main_with_simulate_missing_args(self):
        """Test simulate command with missing required arguments."""
        result = main(["simulate"])
        assert result != 0  # Should fail due to missing required arguments
    
    def test_main_with_validate_missing_args(self):
        """Test validate command with missing required arguments."""
        result = main(["validate"])
        assert result != 0  # Should fail due to missing required arguments
    
    def test_main_with_station_info_missing_args(self):
        """Test station-info command with missing required arguments."""
        result = main(["station-info"])
        assert result != 0  # Should fail due to missing station ID
    
    def test_main_with_list_outputs(self):
        """Test list-outputs command (should work without arguments)."""
        result = main(["list-outputs"])
        # This should succeed even with no station directories
        assert result == 0
    
    def test_main_with_clean_missing_args(self):
        """Test clean command with missing required arguments."""
        result = main(["clean"])
        assert result != 0  # Should fail due to missing target


if __name__ == "__main__":
    pytest.main([__file__, "-v"])