"""
RTL_433 Subprocess Decoder

Runs RTL_433 binary on pulse data and parses JSON output
"""

import json
import subprocess
from pathlib import Path
from typing import List, Dict, Optional
from dataclasses import dataclass, asdict
from loguru import logger

from ..parser.metadata import SignalMetadata
from ..parser.rtl433_converter import get_converter


@dataclass
class RTL433DecodedDevice:
    """Decoded device from RTL_433"""
    model: str
    manufacturer: Optional[str] = None
    device_id: Optional[str] = None
    protocol_id: Optional[int] = None
    frequency: int = 0
    raw_json: Dict = None
    confidence: float = 0.95  # RTL_433 decodes are high confidence

    def __post_init__(self):
        if self.raw_json is None:
            self.raw_json = {}

    @classmethod
    def from_json(cls, data: Dict, frequency: int):
        """Create from RTL_433 JSON output"""
        return cls(
            model=data.get('model', 'Unknown'),
            manufacturer=data.get('manufacturer'),
            device_id=str(data.get('id')) if data.get('id') is not None else None,
            protocol_id=data.get('protocol'),
            frequency=frequency,
            raw_json=data
        )

    def to_dict(self) -> Dict:
        """Convert to dictionary"""
        return {
            'model': self.model,
            'manufacturer': self.manufacturer,
            'device_id': self.device_id,
            'protocol_id': self.protocol_id,
            'frequency': self.frequency,
            'confidence': self.confidence,
            'raw_json': self.raw_json
        }


class RTL433Decoder:
    """
    RTL_433 decoder wrapper

    Converts RAW pulse data and runs rtl_433 binary
    """

    def __init__(self,
                 rtl433_path: str = "rtl_433",
                 timeout: int = 10):
        """
        Initialize decoder

        Args:
            rtl433_path: Path to rtl_433 binary (default: system PATH)
            timeout: Max execution time in seconds
        """
        self.rtl433_path = rtl433_path
        self.timeout = timeout
        self.converter = get_converter()

        # Verify RTL_433 is available
        if not self._check_rtl433_available():
            logger.warning("rtl_433 binary not found in PATH")

    def _check_rtl433_available(self) -> bool:
        """Check if rtl_433 is installed"""
        try:
            result = subprocess.run(
                [self.rtl433_path, '-V'],
                capture_output=True,
                timeout=5,
                text=True
            )
            if result.returncode == 0:
                # Extract version
                version_line = result.stdout.split('\n')[0]
                logger.info(f"RTL_433 available: {version_line}")
                return True
            return False
        except (FileNotFoundError, subprocess.TimeoutExpired):
            return False

    def decode(self,
               metadata: SignalMetadata,
               enable_all_protocols: bool = True,
               specific_protocols: Optional[List[int]] = None) -> List[RTL433DecodedDevice]:
        """
        Decode RAW signal using RTL_433

        Args:
            metadata: Parsed .sub file metadata with RAW_Data
            enable_all_protocols: Try all RTL_433 protocols (slower but comprehensive)
            specific_protocols: List of protocol IDs to enable (faster)

        Returns:
            List of decoded devices (may be empty if no match)
        """
        if not metadata.has_raw_data:
            logger.debug("No RAW data to decode")
            return []

        # Convert to pulse file
        pulse_file = self.converter.convert_metadata(metadata)
        if not pulse_file:
            logger.error("Failed to convert RAW data to pulse file")
            return []

        try:
            # Build RTL_433 command
            cmd = [
                self.rtl433_path,
                '-r', pulse_file,  # Read from file
                '-F', 'json',      # JSON output
                '-M', 'level',     # Add signal level
                '-M', 'protocol',  # Add protocol ID
                '-M', 'time:iso',  # Add timestamp
            ]

            # Protocol selection
            if specific_protocols:
                # Enable only specific protocols
                cmd.extend(['-R', '0'])  # Disable all first
                for proto_id in specific_protocols:
                    cmd.extend(['-R', str(proto_id)])
                logger.debug(f"Enabled protocols: {specific_protocols}")
            elif not enable_all_protocols:
                # Use frequency-based filtering for common bands
                freq_mhz = metadata.frequency / 1_000_000
                protocols = self._get_frequency_protocols(freq_mhz)
                if protocols:
                    cmd.extend(['-R', '0'])
                    for proto_id in protocols:
                        cmd.extend(['-R', str(proto_id)])
                    logger.debug(f"Frequency-based protocols ({freq_mhz:.1f}MHz): {protocols}")

            # Execute
            logger.debug(f"Running: {' '.join(cmd)}")
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=self.timeout
            )

            # Parse output
            devices = self._parse_output(result.stdout, metadata.frequency)

            if devices:
                logger.info(f"RTL_433 decoded {len(devices)} device(s)")
                for dev in devices:
                    logger.info(f"  - {dev.manufacturer or 'Unknown'} {dev.model} (protocol {dev.protocol_id})")
            else:
                logger.debug("RTL_433 found no matches")

            return devices

        except subprocess.TimeoutExpired:
            logger.error(f"RTL_433 timed out after {self.timeout}s")
            return []

        except Exception as e:
            logger.error(f"RTL_433 decode error: {e}")
            return []

        finally:
            # Cleanup temp file
            try:
                if pulse_file and Path(pulse_file).exists():
                    Path(pulse_file).unlink()
                    logger.debug(f"Cleaned up temp file: {pulse_file}")
            except Exception as e:
                logger.warning(f"Failed to cleanup temp file: {e}")

    def _get_frequency_protocols(self, freq_mhz: float) -> List[int]:
        """
        Get protocol IDs based on frequency

        Args:
            freq_mhz: Frequency in MHz

        Returns:
            List of protocol IDs to try
        """
        # Common protocols for different frequency bands
        if 433.0 <= freq_mhz <= 434.0:
            # 433MHz ISM band - most common
            return [
                12,  # Oregon Scientific
                19,  # Nexus/FreeTec temperature
                20,  # Ambient Weather
                32,  # Fine Offset WH1080
                40,  # Acurite 592TXR
                55,  # Oregon Scientific v2/v3
                73,  # LaCrosse TX141
                113, # Fine Offset WH51
            ]
        elif 868.0 <= freq_mhz <= 870.0:
            # 868MHz SRD band (Europe)
            return [
                78,  # Honeywell ActivLink
                88,  # Thermopro TP11
                113, # Fine Offset WH51
            ]
        elif 315.0 <= freq_mhz <= 316.0:
            # 315MHz ISM band (North America)
            return [
                10,  # Acurite 896 Rain Gauge
                11,  # Acurite 609TXC
                40,  # Acurite tower sensor
            ]
        elif 915.0 <= freq_mhz <= 916.0:
            # 915MHz ISM band (North America)
            return [
                40,  # Acurite sensors
                55,  # Oregon Scientific
            ]
        else:
            # Unknown frequency - try all
            return []

    def _parse_output(self, output: str, frequency: int) -> List[RTL433DecodedDevice]:
        """
        Parse RTL_433 JSON output

        Args:
            output: JSON lines from RTL_433 stdout
            frequency: Original frequency from .sub file

        Returns:
            List of decoded devices
        """
        devices = []

        for line in output.strip().split('\n'):
            if not line or not line.startswith('{'):
                continue

            try:
                data = json.loads(line)

                # RTL_433 outputs various message types
                # We only want decoded device messages (have 'model' field)
                if 'model' in data:
                    device = RTL433DecodedDevice.from_json(data, frequency)
                    devices.append(device)
                    logger.debug(f"Parsed device: {device.model}")

            except json.JSONDecodeError as e:
                logger.warning(f"Failed to parse JSON line: {e}")
                logger.debug(f"Line was: {line}")
                continue

        return devices

    def get_supported_protocols(self) -> Dict[int, str]:
        """
        Get list of supported RTL_433 protocols

        Returns:
            Dict mapping protocol ID to name
        """
        try:
            result = subprocess.run(
                [self.rtl433_path, '-R', 'help'],
                capture_output=True,
                text=True,
                timeout=5
            )

            protocols = {}
            for line in result.stdout.split('\n'):
                # Parse lines like: "  [12]  Acurite Tower Sensor"
                line = line.strip()
                if line.startswith('['):
                    try:
                        # Extract protocol ID and name
                        bracket_end = line.index(']')
                        proto_id_str = line[1:bracket_end].strip()
                        proto_id = int(proto_id_str.rstrip('*'))  # Remove asterisk if present
                        proto_name = line[bracket_end+1:].strip()
                        protocols[proto_id] = proto_name
                    except (ValueError, IndexError):
                        continue

            logger.info(f"Found {len(protocols)} supported RTL_433 protocols")
            return protocols

        except Exception as e:
            logger.error(f"Failed to get RTL_433 protocols: {e}")
            return {}

    def get_version(self) -> Optional[str]:
        """Get RTL_433 version string"""
        try:
            result = subprocess.run(
                [self.rtl433_path, '-V'],
                capture_output=True,
                text=True,
                timeout=5
            )
            if result.returncode == 0:
                return result.stdout.split('\n')[0]
            return None
        except Exception:
            return None


# Global singleton
_decoder: Optional[RTL433Decoder] = None


def get_decoder() -> RTL433Decoder:
    """Get global decoder instance"""
    global _decoder
    if _decoder is None:
        _decoder = RTL433Decoder()
    return _decoder


if __name__ == '__main__':
    # Test the decoder
    import sys
    from ..parser.sub_parser import parse_sub_file

    # Setup logging
    logger.remove()
    logger.add(sys.stderr, level="INFO")

    decoder = get_decoder()

    # Show version
    version = decoder.get_version()
    print(f"RTL_433 Version: {version}\n")

    # Show supported protocols
    protocols = decoder.get_supported_protocols()
    print(f"Supported protocols: {len(protocols)}")
    print("Sample protocols:")
    for pid, name in list(protocols.items())[:10]:
        print(f"  [{pid:3d}] {name}")

    # Test decoding if file provided
    if len(sys.argv) > 1:
        test_file = sys.argv[1]
        metadata = parse_sub_file(test_file)

        print(f"\nDecoding: {test_file}")
        print(f"Frequency: {metadata.frequency / 1_000_000:.3f} MHz")
        print(f"Pulses: {metadata.pulse_count}")

        # Try decoding
        devices = decoder.decode(metadata, enable_all_protocols=True)

        if devices:
            print(f"\nDecoded {len(devices)} device(s):")
            for device in devices:
                print(f"\n  Model: {device.model}")
                if device.manufacturer:
                    print(f"  Manufacturer: {device.manufacturer}")
                if device.device_id:
                    print(f"  Device ID: {device.device_id}")
                if device.protocol_id:
                    print(f"  Protocol: {device.protocol_id}")
                print(f"  Confidence: {device.confidence}")
                print(f"  Raw JSON keys: {list(device.raw_json.keys())}")
        else:
            print("\nNo devices decoded")
