"""
Wardriving Data Importer

Imports GPS-tagged SubGhz captures from various formats:
- Wigle-WiFi CSV format (adapted for SubGhz)
- GPS coordinate JSON files
- Batch .sub files with companion GPS data
"""

import csv
import json
from pathlib import Path
from typing import List, Dict, Optional, Tuple
from datetime import datetime
from dataclasses import dataclass
from loguru import logger

from .sub_parser import SubFileParser
from .gps_extractor import GPSFilenameExtractor, GPSCoordinates


@dataclass
class WardrivingCapture:
    """Single wardriving capture with GPS and RF data"""
    filename: str
    frequency: int  # Hz
    protocol: str
    latitude: float
    longitude: float
    altitude: Optional[float] = None
    accuracy: Optional[float] = None
    timestamp: Optional[str] = None
    rssi: Optional[int] = None
    session_id: Optional[str] = None
    gps_source: str = "wardriving"

    def to_dict(self) -> Dict:
        """Convert to dictionary for API"""
        return {
            'filename': self.filename,
            'frequency': self.frequency,
            'protocol': self.protocol,
            'latitude': self.latitude,
            'longitude': self.longitude,
            'altitude': self.altitude,
            'accuracy': self.accuracy,
            'timestamp': self.timestamp,
            'rssi': self.rssi,
            'session_id': self.session_id,
            'gps_source': self.gps_source
        }


class WigleCSVImporter:
    """
    Import SubGhz wardriving data from Wigle-WiFi CSV format

    Format:
    WigleWifi-1.4,appRelease=SubGHz-Wardrive-1.0,...
    SignalHash,Frequency(MHz),Protocol,RSSI(dBm),Latitude,Longitude,Altitude,Accuracy,...
    """

    def __init__(self):
        self.sub_parser = SubFileParser()
        self.captures: List[WardrivingCapture] = []

    def parse_csv(self, csv_path: str) -> List[WardrivingCapture]:
        """Parse Wigle CSV file"""
        path = Path(csv_path)
        if not path.exists():
            raise FileNotFoundError(f"CSV file not found: {csv_path}")

        self.captures = []

        with open(path, 'r') as f:
            # Read header line
            header_line = f.readline().strip()
            if not header_line.startswith('WigleWifi'):
                raise ValueError("Not a valid Wigle CSV file")

            # Read column headers
            reader = csv.DictReader(f)

            for row in reader:
                try:
                    capture = self._parse_row(row, path.parent)
                    if capture:
                        self.captures.append(capture)
                except Exception as e:
                    logger.warning(f"Failed to parse row: {e}")
                    continue

        logger.info(f"Parsed {len(self.captures)} captures from {csv_path}")
        return self.captures

    def _parse_row(self, row: Dict, base_dir: Path) -> Optional[WardrivingCapture]:
        """Parse single CSV row"""
        # Get frequency in Hz
        freq_mhz = float(row.get('Frequency(MHz)', 0))
        frequency = int(freq_mhz * 1e6)

        # Get GPS coordinates
        lat = float(row.get('Latitude', 0))
        lon = float(row.get('Longitude', 0))

        if lat == 0 or lon == 0:
            return None

        # Get .sub filename
        filename = row.get('BruceFile', '')
        if not filename:
            filename = f"capture_{row.get('SignalHash', 'unknown')}.sub"

        # Parse altitude and accuracy if available
        altitude = None
        if row.get('Altitude'):
            try:
                altitude = float(row['Altitude'])
            except:
                pass

        accuracy = None
        if row.get('Accuracy'):
            try:
                accuracy = float(row['Accuracy'])
            except:
                pass

        # Get RSSI
        rssi = None
        if row.get('RSSI(dBm)'):
            try:
                rssi = int(row['RSSI(dBm)'])
            except:
                pass

        return WardrivingCapture(
            filename=filename,
            frequency=frequency,
            protocol=row.get('Protocol', 'Unknown'),
            latitude=lat,
            longitude=lon,
            altitude=altitude,
            accuracy=accuracy,
            timestamp=row.get('FirstSeen', datetime.now().isoformat()),
            rssi=rssi,
            session_id=row.get('SessionID'),
            gps_source='wigle_csv'
        )


class GPSJSONImporter:
    """
    Import GPS coordinates from JSON files

    Format:
    {
      "type": "gps_coordinate",
      "data": {
        "latitude": 34.0522,
        "longitude": -118.2437,
        "accuracy": 5.0,
        "timestamp": "2026-01-09T21:26:51Z"
      }
    }
    """

    @staticmethod
    def parse_json(json_path: str) -> Optional[GPSCoordinates]:
        """Parse GPS JSON file"""
        path = Path(json_path)
        if not path.exists():
            raise FileNotFoundError(f"JSON file not found: {json_path}")

        with open(path, 'r') as f:
            data = json.load(f)

        # Extract GPS data
        gps_data = data.get('data', {})

        lat = gps_data.get('latitude')
        lon = gps_data.get('longitude')

        if lat is None or lon is None:
            return None

        return GPSCoordinates(
            latitude=float(lat),
            longitude=float(lon),
            source='gps_json',
            accuracy=gps_data.get('accuracy'),
            altitude=gps_data.get('altitude')
        )


class BatchImporter:
    """
    Batch import .sub files with companion GPS data

    Supports:
    1. CSV manifest (Wigle format)
    2. Individual JSON GPS files (one per .sub file)
    3. GPS in filenames
    4. Single JSON with multiple GPS points
    """

    def __init__(self):
        self.sub_parser = SubFileParser()
        self.gps_extractor = GPSFilenameExtractor()
        self.csv_importer = WigleCSVImporter()
        self.captures: List[WardrivingCapture] = []

    def import_from_directory(
        self,
        directory: str,
        csv_manifest: Optional[str] = None
    ) -> List[WardrivingCapture]:
        """
        Import all .sub files from directory

        Priority:
        1. CSV manifest (if provided)
        2. Companion JSON files (filename.json for filename.sub)
        3. GPS in filename
        4. Skip file if no GPS found
        """
        dir_path = Path(directory)
        if not dir_path.exists():
            raise FileNotFoundError(f"Directory not found: {directory}")

        self.captures = []

        # Method 1: CSV manifest
        if csv_manifest:
            csv_captures = self.csv_importer.parse_csv(csv_manifest)
            self.captures.extend(csv_captures)
            logger.info(f"Imported {len(csv_captures)} captures from CSV manifest")
            return self.captures

        # Method 2: Process individual .sub files
        sub_files = list(dir_path.glob('*.sub'))
        logger.info(f"Found {len(sub_files)} .sub files")

        for sub_file in sub_files:
            capture = self._import_sub_file(sub_file)
            if capture:
                self.captures.append(capture)

        logger.info(f"Successfully imported {len(self.captures)} captures")
        return self.captures

    def _import_sub_file(self, sub_path: Path) -> Optional[WardrivingCapture]:
        """Import single .sub file with GPS from various sources"""

        # Try to get GPS coordinates
        gps_coords = None

        # 1. Check for companion JSON file
        json_path = sub_path.with_suffix('.json')
        if json_path.exists():
            gps_coords = GPSJSONImporter.parse_json(str(json_path))
            if gps_coords:
                gps_coords.source = 'companion_json'

        # 2. Extract from filename
        if not gps_coords:
            gps_coords = self.gps_extractor.extract(sub_path.name)

        # Skip if no GPS found
        if not gps_coords:
            logger.debug(f"No GPS for {sub_path.name}, skipping")
            return None

        # Parse .sub file for RF metadata
        try:
            metadata = self.sub_parser.parse(str(sub_path))
        except Exception as e:
            logger.warning(f"Failed to parse {sub_path.name}: {e}")
            return None

        return WardrivingCapture(
            filename=sub_path.name,
            frequency=metadata.frequency if hasattr(metadata, 'frequency') else 0,
            protocol=metadata.protocol if hasattr(metadata, 'protocol') else 'Unknown',
            latitude=gps_coords.latitude,
            longitude=gps_coords.longitude,
            altitude=gps_coords.altitude,
            accuracy=gps_coords.accuracy,
            timestamp=datetime.now().isoformat(),
            gps_source=gps_coords.source
        )

    def to_upload_manifest(self) -> Dict:
        """Convert captures to upload manifest format"""
        return {
            "session_uuid": f"batch_import_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
            "captures": [c.to_dict() for c in self.captures]
        }


# Example usage
if __name__ == "__main__":
    # Test Wigle CSV import
    print("=" * 60)
    print("Testing Wigle CSV Import")
    print("=" * 60)

    csv_importer = WigleCSVImporter()
    csv_path = "signatures/t-embed-rf/test_export_wigle.csv"

    if Path(csv_path).exists():
        captures = csv_importer.parse_csv(csv_path)
        print(f"\nImported {len(captures)} captures from CSV")

        for capture in captures:
            print(f"\n  File: {capture.filename}")
            print(f"  Frequency: {capture.frequency / 1e6:.2f} MHz")
            print(f"  GPS: {capture.latitude}, {capture.longitude}")
            print(f"  Accuracy: {capture.accuracy}m")

    # Test batch import from directory
    print("\n" + "=" * 60)
    print("Testing Batch Import")
    print("=" * 60)

    batch_importer = BatchImporter()
    captures = batch_importer.import_from_directory("signatures/t-embed-rf")

    print(f"\nImported {len(captures)} total captures")
    print(f"\nGPS Sources:")
    for source in set(c.gps_source for c in captures):
        count = sum(1 for c in captures if c.gps_source == source)
        print(f"  {source}: {count}")
