"""
lsusb output parser for USB device detection and extraction.
Parses output from 'lsusb -v' and extracts individual device information.
"""
import re
from typing import List, Dict, Any, Optional


def detect_usb_devices(lsusb_output: str) -> List[Dict[str, str]]:
    """
    Detect all USB devices from lsusb -v output.
    Returns a list of devices with their Bus line and basic info.

    Args:
        lsusb_output: Raw output from 'lsusb -v' command

    Returns:
        List of dicts with keys: bus_line, bus, device, id, vendor_id, product_id, description

    Example:
        [
            {
                "bus_line": "Bus 002 Device 003: ID 0781:55ab SanDisk Corp. ...",
                "bus": "002",
                "device": "003",
                "id": "0781:55ab",
                "vendor_id": "0x0781",
                "product_id": "0x55ab",
                "description": "SanDisk Corp. ..."
            },
            ...
        ]
    """
    devices = []
    lines = lsusb_output.strip().split('\n')

    for line in lines:
        line_stripped = line.strip()
        # Match lines starting with "Bus"
        # Format: "Bus 002 Device 003: ID 0781:55ab SanDisk Corp. ..."
        match = re.match(r'^Bus\s+(\d+)\s+Device\s+(\d+):\s+ID\s+([0-9a-fA-F]{4}):([0-9a-fA-F]{4})\s*(.*)$', line_stripped)
        if match:
            bus = match.group(1)
            device_num = match.group(2)
            vendor_id = match.group(3).lower()
            product_id = match.group(4).lower()
            description = match.group(5).strip()

            devices.append({
                "bus_line": line_stripped,
                "bus": bus,
                "device": device_num,
                "id": f"{vendor_id}:{product_id}",
                "vendor_id": f"0x{vendor_id}",
                "product_id": f"0x{product_id}",
                "description": description
            })

    return devices


def extract_device_section(lsusb_output: str, bus: str, device: str) -> Optional[str]:
    """
    Extract the complete section for a specific device from lsusb -v output.

    Args:
        lsusb_output: Raw output from 'lsusb -v' command
        bus: Bus number (e.g., "002")
        device: Device number (e.g., "003")

    Returns:
        Complete section for the device, from its Bus line to the next Bus line (or end)
    """
    lines = lsusb_output.strip().split('\n')

    # Build the pattern to match the target device's Bus line
    target_pattern = re.compile(rf'^Bus\s+{bus}\s+Device\s+{device}:')

    section_lines = []
    in_section = False

    for line in lines:
        # Check if this is the start of our target device
        if target_pattern.match(line):
            in_section = True
            section_lines.append(line)
            continue

        # If we're in the section
        if in_section:
            # Check if we've hit the next device (new Bus line)
            if line.startswith('Bus '):
                # End of our section
                break

            # Add the line to our section
            section_lines.append(line)

    if section_lines:
        return '\n'.join(section_lines)

    return None


def parse_device_info(device_section: str) -> Dict[str, Any]:
    """
    Parse detailed information from a device section.

    Args:
        device_section: The complete lsusb output for a single device

    Returns:
        Dictionary with parsed device information including interface classes
    """
    result = {
        "vendor_id": None,  # idVendor
        "product_id": None,  # idProduct
        "manufacturer": None,  # iManufacturer (fabricant)
        "product": None,  # iProduct (modele)
        "serial": None,
        "usb_version": None,  # bcdUSB (declared version)
        "device_class": None,  # bDeviceClass
        "device_subclass": None,
        "device_protocol": None,
        "interface_classes": [],  # CRITICAL: bInterfaceClass from all interfaces
        "max_power": None,  # MaxPower (in mA)
        "speed": None,  # Negotiated speed (determines actual USB type)
        "usb_type": None,  # Determined from negotiated speed
        "requires_firmware": False,  # True if any interface is Vendor Specific (255)
        "is_bus_powered": None,
        "is_self_powered": None,
        "power_sufficient": None  # Based on MaxPower vs port capacity
    }

    lines = device_section.split('\n')

    # Parse the first line (Bus line) - contains idVendor:idProduct and vendor name
    # Format: "Bus 002 Device 005: ID 0bda:8176 Realtek Semiconductor Corp."
    first_line = lines[0] if lines else ""
    bus_match = re.match(r'^Bus\s+\d+\s+Device\s+\d+:\s+ID\s+([0-9a-fA-F]{4}):([0-9a-fA-F]{4})\s*(.*)$', first_line)
    if bus_match:
        result["vendor_id"] = f"0x{bus_match.group(1).lower()}"
        result["product_id"] = f"0x{bus_match.group(2).lower()}"
        # Extract vendor name from first line (marque = text after IDs)
        vendor_name = bus_match.group(3).strip()
        if vendor_name:
            result["manufacturer"] = vendor_name

    # Parse detailed fields
    current_interface = False
    for line in lines[1:]:
        line_stripped = line.strip()

        # iManufacturer (fabricant)
        mfg_match = re.search(r'iManufacturer\s+\d+\s+(.+?)$', line_stripped)
        if mfg_match:
            result["manufacturer"] = mfg_match.group(1).strip()

        # iProduct (modele)
        prod_match = re.search(r'iProduct\s+\d+\s+(.+?)$', line_stripped)
        if prod_match:
            result["product"] = prod_match.group(1).strip()

        # iSerial
        serial_match = re.search(r'iSerial\s+\d+\s+(.+?)$', line_stripped)
        if serial_match:
            result["serial"] = serial_match.group(1).strip()

        # bcdUSB (declared version, not definitive)
        usb_ver_match = re.search(r'bcdUSB\s+([\d.]+)', line_stripped)
        if usb_ver_match:
            result["usb_version"] = usb_ver_match.group(1).strip()

        # bDeviceClass
        class_match = re.search(r'bDeviceClass\s+(\d+)\s+(.+?)$', line_stripped)
        if class_match:
            result["device_class"] = class_match.group(1).strip()

        # bDeviceSubClass
        subclass_match = re.search(r'bDeviceSubClass\s+(\d+)', line_stripped)
        if subclass_match:
            result["device_subclass"] = subclass_match.group(1).strip()

        # bDeviceProtocol
        protocol_match = re.search(r'bDeviceProtocol\s+(\d+)', line_stripped)
        if protocol_match:
            result["device_protocol"] = protocol_match.group(1).strip()

        # MaxPower (extract numeric value in mA)
        power_match = re.search(r'MaxPower\s+(\d+)\s*mA', line_stripped)
        if power_match:
            result["max_power"] = power_match.group(1).strip()

        # bmAttributes (to determine Bus/Self powered)
        attr_match = re.search(r'bmAttributes\s+0x([0-9a-fA-F]+)', line_stripped)
        if attr_match:
            attrs = int(attr_match.group(1), 16)
            # Bit 6: Self Powered, Bit 5: Remote Wakeup
            result["is_self_powered"] = bool(attrs & 0x40)
            result["is_bus_powered"] = not result["is_self_powered"]

        # CRITICAL: bInterfaceClass (this determines Mass Storage, not bDeviceClass)
        interface_class_match = re.search(r'bInterfaceClass\s+(\d+)\s+(.+?)$', line_stripped)
        if interface_class_match:
            class_code = int(interface_class_match.group(1))
            class_name = interface_class_match.group(2).strip()
            result["interface_classes"].append({
                "code": class_code,
                "name": class_name
            })

            # Check for Vendor Specific (255) - requires firmware
            if class_code == 255:
                result["requires_firmware"] = True

        # Detect negotiated speed (determines actual USB type)
        # Format can be: "Device Qualifier (for other device speed):" or speed mentioned
        speed_patterns = [
            (r'1\.5\s*Mb(?:it)?/s|Low\s+Speed', 'Low Speed', 'USB 1.1'),
            (r'12\s*Mb(?:it)?/s|Full\s+Speed', 'Full Speed', 'USB 1.1'),
            (r'480\s*Mb(?:it)?/s|High\s+Speed', 'High Speed', 'USB 2.0'),
            (r'5000\s*Mb(?:it)?/s|5\s*Gb(?:it)?/s|SuperSpeed(?:\s+USB)?(?:\s+Gen\s*1)?', 'SuperSpeed', 'USB 3.0'),
            (r'10\s*Gb(?:it)?/s|SuperSpeed\s+USB\s+Gen\s*2|SuperSpeed\+', 'SuperSpeed+', 'USB 3.1'),
            (r'20\s*Gb(?:it)?/s|SuperSpeed\s+USB\s+Gen\s*2x2', 'SuperSpeed Gen 2x2', 'USB 3.2'),
        ]

        for pattern, speed_name, usb_type in speed_patterns:
            if re.search(pattern, line_stripped, re.IGNORECASE):
                result["speed"] = speed_name
                result["usb_type"] = usb_type
                break

    # Determine power sufficiency based on USB type and MaxPower
    if result["max_power"]:
        max_power_ma = int(result["max_power"])
        usb_type = result.get("usb_type", "USB 2.0")  # Default to USB 2.0

        # Normative port capacities
        if "USB 3" in usb_type:
            port_capacity = 900  # USB 3.x: 900 mA @ 5V = 4.5W
        else:
            port_capacity = 500  # USB 2.0: 500 mA @ 5V = 2.5W

        result["power_sufficient"] = max_power_ma <= port_capacity

    return result
