"""
RTL_433 Pulse Data Converter

Converts Flipper .sub RAW_Data to RTL_433 pulse data format (am.s16)
"""

import struct
import tempfile
from pathlib import Path
from typing import List, Optional, Tuple
from loguru import logger

from .metadata import SignalMetadata


class RTL433Converter:
    """
    Convert Flipper RAW timing data to RTL_433 pulse format

    RTL_433 Pulse Format (am.s16):
    - 16-bit signed integers (little-endian)
    - Positive = high pulse duration (μs)
    - Negative = low pulse duration (μs)
    - Direct 1:1 mapping from Flipper RAW_Data
    """

    MAX_PULSE_VALUE = 32767  # int16_t max
    MIN_PULSE_VALUE = -32768  # int16_t min

    def __init__(self, temp_dir: Optional[str] = None):
        """
        Initialize converter

        Args:
            temp_dir: Directory for temporary files (default: /tmp/giglez_rtl433)
        """
        if temp_dir is None:
            self.temp_dir = Path("/tmp/giglez_rtl433")
        else:
            self.temp_dir = Path(temp_dir)

        self.temp_dir.mkdir(parents=True, exist_ok=True)

    def convert_to_pulse_file(self,
                             raw_data: List[int],
                             output_path: str,
                             frequency: Optional[int] = None) -> bool:
        """
        Convert RAW_Data array to RTL_433 pulse data file

        Args:
            raw_data: Array of timing values from .sub file
            output_path: Path to output .am.s16 file
            frequency: Optional frequency for filename (not used in binary format)

        Returns:
            True if successful, False otherwise
        """
        try:
            # Validate input
            if not raw_data:
                logger.error("Empty RAW_Data array")
                return False

            # Validate pulse data
            is_valid, errors = self.validate_pulse_data(raw_data)
            if not is_valid:
                logger.warning(f"Pulse data validation warnings: {errors}")
                # Continue anyway - some signals may have unusual patterns

            # Clamp values to int16_t range
            clamped_data = []
            clamped_count = 0
            for value in raw_data:
                if value > self.MAX_PULSE_VALUE:
                    logger.debug(f"Pulse value {value} exceeds max, clamping to {self.MAX_PULSE_VALUE}")
                    clamped_data.append(self.MAX_PULSE_VALUE)
                    clamped_count += 1
                elif value < self.MIN_PULSE_VALUE:
                    logger.debug(f"Pulse value {value} below min, clamping to {self.MIN_PULSE_VALUE}")
                    clamped_data.append(self.MIN_PULSE_VALUE)
                    clamped_count += 1
                else:
                    clamped_data.append(value)

            if clamped_count > 0:
                logger.warning(f"Clamped {clamped_count} pulse values to int16 range")

            # Pack as little-endian int16 array
            binary_data = struct.pack(f'<{len(clamped_data)}h', *clamped_data)

            # Write to file
            Path(output_path).parent.mkdir(parents=True, exist_ok=True)
            with open(output_path, 'wb') as f:
                f.write(binary_data)

            logger.info(f"Converted {len(raw_data)} pulses to {output_path} ({len(binary_data)} bytes)")
            return True

        except Exception as e:
            logger.error(f"Failed to convert pulse data: {e}")
            return False

    def convert_metadata(self, metadata: SignalMetadata) -> Optional[str]:
        """
        Convert SignalMetadata RAW_Data to temp pulse file

        Args:
            metadata: Parsed .sub file metadata

        Returns:
            Path to temporary .am.s16 file, or None if failed
        """
        if not metadata.has_raw_data or not metadata.raw_data:
            logger.debug("No RAW data in metadata")
            return None

        # Create temp file with frequency in filename
        freq_mhz = metadata.frequency / 1_000_000
        temp_file = self.temp_dir / f"pulse_{freq_mhz:.3f}MHz.am.s16"

        if self.convert_to_pulse_file(metadata.raw_data, str(temp_file), metadata.frequency):
            return str(temp_file)

        return None

    def validate_pulse_data(self, raw_data: List[int]) -> Tuple[bool, List[str]]:
        """
        Validate RAW_Data before conversion

        Args:
            raw_data: Array of timing values

        Returns:
            (is_valid, error_messages)
        """
        errors = []

        if not raw_data:
            errors.append("Empty pulse data")
            return (False, errors)

        # Check for minimum length
        if len(raw_data) < 10:
            errors.append(f"Too few pulses: {len(raw_data)} (minimum 10 recommended)")

        # Check for all zeros
        if all(p == 0 for p in raw_data):
            errors.append("All pulse values are zero")
            return (False, errors)

        # Check for reasonable pulse widths (< 100ms)
        max_pulse = max(abs(p) for p in raw_data)
        if max_pulse > 100_000:
            errors.append(f"Suspiciously large pulse: {max_pulse}μs (>100ms)")

        # Check for alternating sign pattern (typical OOK/ASK)
        positive_count = sum(1 for p in raw_data if p > 0)
        negative_count = sum(1 for p in raw_data if p < 0)

        if positive_count == 0:
            errors.append("No positive (high) pulses detected")

        if negative_count == 0:
            errors.append("No negative (low) pulses detected")

        # Check for very short pulses (< 5μs may be noise)
        very_short = [p for p in raw_data if 0 < abs(p) < 5]
        if len(very_short) > len(raw_data) * 0.5:
            errors.append(f"Too many very short pulses: {len(very_short)} (may be noise)")

        return (len(errors) == 0, errors)

    def get_pulse_statistics(self, raw_data: List[int]) -> dict:
        """
        Get statistics about pulse data

        Args:
            raw_data: Array of timing values

        Returns:
            Dictionary of statistics
        """
        if not raw_data:
            return {}

        positive_pulses = [p for p in raw_data if p > 0]
        negative_pulses = [abs(p) for p in raw_data if p < 0]

        stats = {
            'total_pulses': len(raw_data),
            'positive_count': len(positive_pulses),
            'negative_count': len(negative_pulses),
            'duration_ms': sum(abs(p) for p in raw_data) / 1000.0,
        }

        if positive_pulses:
            stats['avg_high_pulse'] = sum(positive_pulses) / len(positive_pulses)
            stats['max_high_pulse'] = max(positive_pulses)
            stats['min_high_pulse'] = min(positive_pulses)

        if negative_pulses:
            stats['avg_low_pulse'] = sum(negative_pulses) / len(negative_pulses)
            stats['max_low_pulse'] = max(negative_pulses)
            stats['min_low_pulse'] = min(negative_pulses)

        return stats


# Global singleton instance
_converter: Optional[RTL433Converter] = None


def get_converter() -> RTL433Converter:
    """Get global converter instance"""
    global _converter
    if _converter is None:
        _converter = RTL433Converter()
    return _converter


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

    if len(sys.argv) < 2:
        print("Usage: python3 -m src.parser.rtl433_converter <file.sub>")
        sys.exit(1)

    converter = RTL433Converter()
    metadata = parse_sub_file(sys.argv[1])

    print(f"File: {sys.argv[1]}")
    print(f"Frequency: {metadata.frequency / 1_000_000:.3f} MHz")
    print(f"Pulses: {metadata.pulse_count}")

    if metadata.has_raw_data:
        stats = converter.get_pulse_statistics(metadata.raw_data)
        print(f"\nPulse Statistics:")
        for key, value in stats.items():
            print(f"  {key}: {value}")

        is_valid, errors = converter.validate_pulse_data(metadata.raw_data)
        if errors:
            print(f"\nValidation Issues:")
            for err in errors:
                print(f"  - {err}")

        output_path = converter.convert_metadata(metadata)
        if output_path:
            print(f"\nCreated: {output_path}")
        else:
            print("\nConversion failed")
    else:
        print("No RAW data to convert")
