"""
Station discovery command implementation.

Implements the 'precipgen discover' command for finding GHCN weather stations
based on location or coordinates with interactive station selection.
"""

import argparse
from typing import List, Tuple, Optional
import sys
import argparse
from pathlib import Path

from precipgen.utils import get_logger
from precipgen.data.ghcn_downloader import find_nearby_stations
from precipgen.utils.directory_manager import DirectoryManager


def add_parser(subparsers) -> argparse.ArgumentParser:
    """
    Add discover command parser to subparsers.
    
    Args:
        subparsers: ArgumentParser subparsers object
        
    Returns:
        The discover command parser
    """
    parser = subparsers.add_parser(
        "discover",
        help="Discover GHCN weather stations near a location",
        description="Find and rank GHCN weather stations based on location or coordinates",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  precipgen discover --location "Salt Lake City, UT"
  precipgen discover --location "Seattle, WA" --radius 75
  precipgen discover --lat 40.7589 --lon -111.8883
  precipgen discover --lat 47.6062 --lon -122.3321 --radius 50 --output-dir /path/to/analysis
        """
    )
    
    # Location input options (mutually exclusive)
    location_group = parser.add_mutually_exclusive_group(required=True)
    location_group.add_argument(
        "--location", "-l",
        type=str,
        help="City and state (e.g., 'Salt Lake City, UT')"
    )
    location_group.add_argument(
        "--coordinates", "-c",
        nargs=2,
        type=float,
        metavar=("LAT", "LON"),
        help="Latitude and longitude coordinates"
    )
    
    # Individual coordinate options (alternative to --coordinates)
    parser.add_argument(
        "--lat",
        type=float,
        help="Latitude (-90 to 90)"
    )
    parser.add_argument(
        "--lon", 
        type=float,
        help="Longitude (-180 to 180)"
    )
    
    # Search options
    parser.add_argument(
        "--radius", "-r",
        type=float,
        default=50.0,
        help="Search radius in kilometers (default: 50)"
    )
    
    # Output options
    parser.add_argument(
        "--output-dir", "-o",
        type=str,
        help="Custom output directory (default: ./precipgen_output)"
    )
    
    parser.add_argument(
        "--interactive", "-i",
        action="store_true",
        help="Enable interactive station selection"
    )
    
    parser.add_argument(
        "--max-results", "-n",
        type=int,
        default=10,
        help="Maximum number of stations to display (default: 10)"
    )
    
    return parser


def validate_coordinates(lat: float, lon: float) -> Tuple[bool, Optional[str]]:
    """
    Validate latitude and longitude coordinates.
    
    Args:
        lat: Latitude value
        lon: Longitude value
        
    Returns:
        Tuple of (is_valid, error_message)
    """
    if not (-90 <= lat <= 90):
        return False, f"Invalid latitude {lat}: must be between -90 and 90"
    
    if not (-180 <= lon <= 180):
        return False, f"Invalid longitude {lon}: must be between -180 and 180"
    
    return True, None


def geocode_location(location: str) -> Tuple[Optional[Tuple[float, float]], Optional[str]]:
    """
    Geocode a location string to coordinates.
    
    Args:
        location: Location string (e.g., "Salt Lake City, UT")
        
    Returns:
        Tuple of ((lat, lon), error_message)
    """
    # This is a simplified geocoding implementation
    # In a full implementation, this would use a geocoding service
    
    # Known locations for testing
    known_locations = {
        "salt lake city, ut": (40.7589, -111.8883),
        "seattle, wa": (47.6062, -122.3321),
        "portland, or": (45.5152, -122.6784),
        "denver, co": (39.7392, -104.9903),
        "phoenix, az": (33.4484, -112.0740)
    }
    
    location_lower = location.lower().strip()
    
    if location_lower in known_locations:
        return known_locations[location_lower], None
    
    # Check for partial matches
    for known_loc, coords in known_locations.items():
        if location_lower in known_loc or known_loc in location_lower:
            return coords, None
    
    # Location not found
    suggestions = list(known_locations.keys())[:3]
    error_msg = f"Location not found: '{location}'. Did you mean one of: {', '.join(suggestions)}?"
    
    return None, error_msg


def display_station_candidates(stations: List, max_results: int = 10) -> None:
    """
    Display station candidates in a formatted table.
    
    Args:
        stations: List of station candidate objects
        max_results: Maximum number of stations to display
    """
    if not stations:
        print("No stations found within the specified radius.")
        return
    
    print(f"\nFound {len(stations)} stations within search radius:")
    print("-" * 80)
    print(f"{'#':<3} {'Station ID':<12} {'Name':<25} {'Distance':<10} {'Years':<8} {'Quality':<8}")
    print("-" * 80)
    
    for i, station in enumerate(stations[:max_results], 1):
        # Extract station information (this depends on the actual station object structure)
        station_id = getattr(station, 'station_id', 'Unknown')
        name = getattr(station, 'name', 'Unknown')[:24]  # Truncate long names
        distance = getattr(station, 'distance_km', 0)
        years = getattr(station, 'data_years', 0)
        quality = getattr(station, 'quality_score', 0)
        
        print(f"{i:<3} {station_id:<12} {name:<25} {distance:<10.1f} {years:<8} {quality:<8.2f}")
    
    if len(stations) > max_results:
        print(f"\n... and {len(stations) - max_results} more stations")


def interactive_station_selection(stations: List) -> Optional[str]:
    """
    Prompt user to select a station interactively.
    
    Args:
        stations: List of station candidates
        
    Returns:
        Selected station ID or None if cancelled
    """
    if not stations:
        return None
    
    while True:
        try:
            print(f"\nSelect a station (1-{len(stations)}) or 'q' to quit:")
            choice = input("> ").strip().lower()
            
            if choice == 'q':
                return None
            
            station_num = int(choice)
            if 1 <= station_num <= len(stations):
                selected_station = stations[station_num - 1]
                station_id = getattr(selected_station, 'station_id', None)
                return station_id
            else:
                print(f"Please enter a number between 1 and {len(stations)}")
                
        except ValueError:
            print("Please enter a valid number or 'q' to quit")
        except KeyboardInterrupt:
            print("\nSelection cancelled")
            return None


def execute(args) -> int:
    """
    Execute the discover command.
    
    Args:
        args: Parsed command line arguments
        
    Returns:
        Exit code (0 for success, non-zero for error)
    """
    logger = get_logger("precipgen.cli.discover")
    
    try:
        # Determine coordinates
        coordinates = None
        
        if args.location:
            # Geocode location
            coords, error = geocode_location(args.location)
            if error:
                print(f"Error: {error}")
                print("\nSuggested locations:")
                print("  • Salt Lake City, UT")
                print("  • Seattle, WA") 
                print("  • Portland, OR")
                print("  • Denver, CO")
                print("  • Phoenix, AZ")
                print("\nAlternatively, use coordinates:")
                print("  precipgen discover --lat 40.7 --lon -111.9")
                return 1
            coordinates = coords
            
        elif args.coordinates:
            # Use provided coordinates
            lat, lon = args.coordinates
            valid, error = validate_coordinates(lat, lon)
            if not valid:
                print(f"Error: {error}")
                print("\nValid coordinate ranges:")
                print("  • Latitude: -90 to 90 degrees")
                print("  • Longitude: -180 to 180 degrees")
                print("\nExample:")
                print("  precipgen discover --coordinates 40.7589 -111.8883")
                return 1
            coordinates = (lat, lon)
            
        elif args.lat is not None and args.lon is not None:
            # Use individual lat/lon arguments
            valid, error = validate_coordinates(args.lat, args.lon)
            if not valid:
                print(f"Error: {error}")
                print("\nValid coordinate ranges:")
                print("  • Latitude: -90 to 90 degrees")
                print("  • Longitude: -180 to 180 degrees")
                print("\nExample:")
                print("  precipgen discover --lat 40.7589 --lon -111.8883")
                return 1
            coordinates = (args.lat, args.lon)
            
        else:
            print("Error: Must provide either --location, --coordinates, or --lat/--lon")
            print("\nUsage examples:")
            print("  precipgen discover --location \"Salt Lake City, UT\"")
            print("  precipgen discover --lat 40.7589 --lon -111.8883")
            print("  precipgen discover --coordinates 40.7589 -111.8883")
            print("\nFor more help:")
            print("  precipgen discover --help")
            return 1
        
        lat, lon = coordinates
        
        # Validate radius
        if args.radius <= 0:
            print("Error: Search radius must be positive")
            print("Example: --radius 50")
            return 1
        
        if args.radius > 200:
            print(f"Warning: Large search radius ({args.radius} km) may include climatologically different stations")
            print("Consider using a smaller radius (e.g., --radius 50) for more relevant results")
        
        # Find nearby stations
        print(f"Searching for stations near ({lat:.4f}, {lon:.4f}) within {args.radius} km...")
        
        try:
            stations = find_nearby_stations(lat, lon, radius_km=args.radius)
        except Exception as e:
            logger.error(f"Failed to find stations: {e}")
            print(f"Error: Failed to find stations - {e}")
            print("\nTroubleshooting:")
            print("  • Check your internet connection")
            print("  • Try a different location or larger radius")
            print("  • Verify coordinates are valid")
            return 1
        
        if not stations:
            print("No stations found within the specified radius.")
            print("\nSuggestions:")
            print(f"  • Try increasing the search radius: --radius {int(args.radius * 1.5)}")
            print("  • Try a different location")
            print("  • Check if coordinates are in a populated area")
            return 1
        
        # Display results
        display_station_candidates(stations, args.max_results)
        
        # Interactive selection if requested
        if args.interactive:
            selected_station_id = interactive_station_selection(stations)
            
            if selected_station_id:
                print(f"\nSelected station: {selected_station_id}")
                
                # Initialize directory manager for output organization
                dir_manager = DirectoryManager(args.output_dir or "./precipgen_output")
                station_dir = dir_manager.create_station_directory(selected_station_id)
                
                print(f"Station directory created: {station_dir}")
                print(f"\nNext steps:")
                print(f"  1. Download data: precipgen download --station {selected_station_id}")
                print(f"  2. Analyze data: precipgen analyze --station {selected_station_id}")
                
            else:
                print("No station selected.")
                print("You can run the command again with --interactive to select a station")
                return 1
        else:
            # Non-interactive mode - just display results
            if stations:
                best_station = stations[0]
                station_id = getattr(best_station, 'station_id', 'Unknown')
                print(f"\nBest station: {station_id}")
                print(f"Use --interactive flag to select a different station")
                print(f"\nNext steps:")
                print(f"  1. Use interactive mode: precipgen discover --location \"{args.location or f'{lat}, {lon}'}\" --interactive")
                print(f"  2. Or proceed with best station: precipgen analyze --station {station_id}")
        
        return 0
        
    except KeyboardInterrupt:
        print("\nOperation cancelled by user")
        return 130
    except Exception as e:
        logger.error(f"Unexpected error in discover command: {e}")
        print(f"Error: {e}")
        print("\nIf this error persists, please check:")
        print("  • Your internet connection")
        print("  • That the location/coordinates are valid")
        print("  • Try running with --verbose for more details")
        return 1