"""
GPS Coordinate Extraction from Filenames

Automatically extracts GPS coordinates from filename patterns
"""

import re
from pathlib import Path
from typing import Optional, Dict, Tuple
from dataclasses import dataclass


@dataclass
class GPSCoordinates:
    """GPS coordinates extracted from filename"""
    latitude: float
    longitude: float
    source: str = "filename"
    accuracy: Optional[float] = None
    altitude: Optional[float] = None

    def to_dict(self) -> Dict:
        """Convert to dictionary"""
        return {
            'latitude': self.latitude,
            'longitude': self.longitude,
            'source': self.source,
            'accuracy': self.accuracy,
            'altitude': self.altitude
        }


class GPSFilenameExtractor:
    """
    Extract GPS coordinates from various filename patterns

    Supported patterns:
    1. Decimal degrees: 34.0478N_118.2348W_timestamp_name.sub
    2. Signed decimal: -34.0478_118.2348_timestamp_name.sub
    3. DMS format: 34d02m52sN_118d14m05sW_name.sub
    4. Compact: lat34.0478lon-118.2348_name.sub
    """

    # Pattern 1: Decimal degrees with N/S/E/W (most common)
    PATTERN_NSEW = re.compile(
        r'(\d+\.?\d*)([NS])_(\d+\.?\d*)([EW])',
        re.IGNORECASE
    )

    # Pattern 2: Signed decimal degrees
    PATTERN_SIGNED = re.compile(
        r'(-?\d+\.?\d+)_(-?\d+\.?\d+)'
    )

    # Pattern 3: lat/lon prefix format
    PATTERN_LATLON = re.compile(
        r'lat(-?\d+\.?\d+)lon(-?\d+\.?\d+)',
        re.IGNORECASE
    )

    # Pattern 4: DMS (Degrees Minutes Seconds)
    PATTERN_DMS = re.compile(
        r'(\d+)d(\d+)m(\d+\.?\d*)s([NS])_(\d+)d(\d+)m(\d+\.?\d*)s([EW])',
        re.IGNORECASE
    )

    def extract(self, filename: str) -> Optional[GPSCoordinates]:
        """
        Extract GPS coordinates from filename

        Args:
            filename: Filename to parse (with or without path)

        Returns:
            GPSCoordinates if found, None otherwise
        """
        # Get just the filename without path
        basename = Path(filename).name

        # Try each pattern
        coords = (
            self._try_nsew(basename) or
            self._try_latlon(basename) or
            self._try_signed(basename) or
            self._try_dms(basename)
        )

        return coords

    def _try_nsew(self, filename: str) -> Optional[GPSCoordinates]:
        """Try N/S/E/W pattern: 34.0478N_118.2348W"""
        match = self.PATTERN_NSEW.search(filename)
        if not match:
            return None

        lat_val, lat_dir, lon_val, lon_dir = match.groups()

        # Convert to float
        lat = float(lat_val)
        lon = float(lon_val)

        # Apply direction (N=positive, S=negative, E=positive, W=negative)
        if lat_dir.upper() == 'S':
            lat = -lat
        if lon_dir.upper() == 'W':
            lon = -lon

        # Validate ranges
        if not self._validate_coordinates(lat, lon):
            return None

        return GPSCoordinates(
            latitude=lat,
            longitude=lon,
            source="filename_nsew"
        )

    def _try_signed(self, filename: str) -> Optional[GPSCoordinates]:
        """Try signed decimal: -34.0478_118.2348"""
        match = self.PATTERN_SIGNED.search(filename)
        if not match:
            return None

        lat, lon = match.groups()
        lat = float(lat)
        lon = float(lon)

        if not self._validate_coordinates(lat, lon):
            return None

        return GPSCoordinates(
            latitude=lat,
            longitude=lon,
            source="filename_signed"
        )

    def _try_latlon(self, filename: str) -> Optional[GPSCoordinates]:
        """Try lat/lon prefix: lat34.0478lon-118.2348"""
        match = self.PATTERN_LATLON.search(filename)
        if not match:
            return None

        lat, lon = match.groups()
        lat = float(lat)
        lon = float(lon)

        if not self._validate_coordinates(lat, lon):
            return None

        return GPSCoordinates(
            latitude=lat,
            longitude=lon,
            source="filename_latlon"
        )

    def _try_dms(self, filename: str) -> Optional[GPSCoordinates]:
        """Try DMS format: 34d02m52sN_118d14m05sW"""
        match = self.PATTERN_DMS.search(filename)
        if not match:
            return None

        lat_deg, lat_min, lat_sec, lat_dir, lon_deg, lon_min, lon_sec, lon_dir = match.groups()

        # Convert DMS to decimal degrees
        lat = self._dms_to_decimal(
            int(lat_deg), int(lat_min), float(lat_sec)
        )
        lon = self._dms_to_decimal(
            int(lon_deg), int(lon_min), float(lon_sec)
        )

        # Apply direction
        if lat_dir.upper() == 'S':
            lat = -lat
        if lon_dir.upper() == 'W':
            lon = -lon

        if not self._validate_coordinates(lat, lon):
            return None

        return GPSCoordinates(
            latitude=lat,
            longitude=lon,
            source="filename_dms"
        )

    @staticmethod
    def _dms_to_decimal(degrees: int, minutes: int, seconds: float) -> float:
        """Convert DMS to decimal degrees"""
        return degrees + (minutes / 60.0) + (seconds / 3600.0)

    @staticmethod
    def _validate_coordinates(lat: float, lon: float) -> bool:
        """Validate coordinate ranges"""
        return -90 <= lat <= 90 and -180 <= lon <= 180


def extract_gps_from_filename(filename: str) -> Optional[Dict]:
    """
    Convenience function to extract GPS from filename

    Args:
        filename: Filename to parse

    Returns:
        Dictionary with GPS data or None

    Example:
        >>> extract_gps_from_filename("34.0478N_118.2348W_capture.sub")
        {'latitude': 34.0478, 'longitude': -118.2348, 'source': 'filename_nsew'}
    """
    extractor = GPSFilenameExtractor()
    coords = extractor.extract(filename)
    return coords.to_dict() if coords else None


# Example usage and testing
if __name__ == "__main__":
    test_filenames = [
        "34.0478N_118.2348W_1637_raw_8.sub",  # N/S/E/W format
        "34.0478N_118.2349W_1351_raw_10.sub",
        "lat34.0478lon-118.2348_test.sub",    # lat/lon prefix
        "-34.0478_118.2348_capture.sub",      # Signed decimal
        "34d02m52sN_118d14m05sW_test.sub",   # DMS format
        "raw_7.sub",                          # No GPS
    ]

    extractor = GPSFilenameExtractor()

    print("GPS Filename Extraction Test")
    print("=" * 60)

    for filename in test_filenames:
        coords = extractor.extract(filename)
        if coords:
            print(f"✅ {filename}")
            print(f"   Lat: {coords.latitude:.6f}, Lon: {coords.longitude:.6f}")
            print(f"   Source: {coords.source}")
        else:
            print(f"⏭️  {filename} - No GPS found")
        print()
