"""
Switch Downgrader — Hardware Detection.
Handles eMMC drive detection, SD card detection, and console model identification.
"""
import os
import re


def detect_switch_drives_wmi(log_func=None):
    """Detect Switch eMMC/emuMMC drives via WMI using Hekate hardware IDs."""
    try:
        import pythoncom
        import wmi
        pythoncom.CoInitialize()
        c = wmi.WMI()
    except Exception as e:
        if log_func:
            log_func(f"ERROR: WMI initialization failed: {e}")
        return []

    drives = []
    if log_func:
        log_func("--- Scanning for Switch eMMC using Hekate hardware IDs...")

    try:
        for disk in c.Win32_DiskDrive():
            pnp_id = (disk.PNPDeviceID or "").upper()

            # Hekate exposes the eMMC with specific PNP identifiers:
            #   USBSTOR\DISK&VEN_HEKATE&PROD_EMMC_GPP&REV_1.00\...
            #   USBSTOR\DISK&VEN_HEKATE&PROD_SD_GPP&REV_1.00\...  (emuMMC)
            is_emmc = "VEN_HEKATE" in pnp_id and "PROD_EMMC_GPP" in pnp_id
            is_emummc = "VEN_HEKATE" in pnp_id and "PROD_SD_GPP" in pnp_id

            if is_emmc or is_emummc:
                try:
                    size_gb = int(disk.Size) / (1024**3)
                    drive_type = "eMMC GPP" if is_emmc else "emuMMC Raw (SD GPP)"
                    drive_info = {
                        'path': disk.DeviceID,
                        'size': f"{size_gb:.2f} GB",
                        'size_gb': size_gb,
                        'model': disk.Model or "Unknown",
                        'type': drive_type
                    }
                    drives.append(drive_info)
                    if log_func:
                        log_func(f"   Found: {drive_type} — {size_gb:.2f}GB — {drive_info['model']}")
                except Exception as e:
                    if log_func:
                        log_func(f"WARNING: Found Hekate device but could not read size: {e}")
                    continue
    except Exception as e:
        if log_func:
            log_func(f"ERROR scanning drives: {e}")

    if not drives and log_func:
        log_func("   No Switch eMMC drives found.")
    return drives


def detect_switch_sd_card_wmi(log_func=None):
    """Detect Switch SD card exposed via Hekate using WMI associations."""
    if log_func:
        log_func("--- Detecting Switch SD card via Hekate...")
    try:
        import pythoncom
        import wmi
        pythoncom.CoInitialize()
        c = wmi.WMI()

        found_mountpoints = []

        # Walk: LogicalDisk → Partition → PhysicalDisk, then check PNP ID
        for logical_disk in c.Win32_LogicalDisk():
            try:
                partitions = c.query(
                    f"ASSOCIATORS OF {{Win32_LogicalDisk.DeviceID='{logical_disk.DeviceID}'}}"
                    f" WHERE AssocClass=Win32_LogicalDiskToPartition")
                if not partitions:
                    continue

                physical_disks = c.query(
                    f"ASSOCIATORS OF {{Win32_DiskPartition.DeviceID='{partitions[0].DeviceID}'}}"
                    f" WHERE AssocClass=Win32_DiskDriveToDiskPartition")
                if not physical_disks:
                    continue

                pnp_id = (physical_disks[0].PNPDeviceID or "").upper()
                if "VEN_HEKATE" in pnp_id and "PROD_SD_RAW" in pnp_id:
                    mountpoint = logical_disk.DeviceID
                    if log_func:
                        log_func(f"   Found Hekate SD Card partition at {mountpoint}")
                    found_mountpoints.append(mountpoint)
            except Exception:
                continue

        if found_mountpoints:
            return found_mountpoints

    except Exception as e:
        if log_func:
            log_func(f"ERROR during SD detection: {e}")

    if log_func:
        log_func("   No Hekate SD card found.")
    return []


def detect_console_model(prodinfo_path, log_func=None):
    """
    Detect console model from a decrypted PRODINFO file.
    Returns: (model_name, is_mariko) tuple.
    """
    MODEL_MAP = {
        1: ("Erista (V1)", False),
        3: ("Mariko (V2)", True),
        4: ("Lite", True),
        6: ("OLED", True),
    }

    try:
        with open(prodinfo_path, 'rb') as f:
            magic = f.read(4)
            if magic != b'CAL0':
                if log_func:
                    log_func("ERROR: PRODINFO file is invalid or encrypted (magic is not CAL0).")
                return None, None

            f.seek(0x3740)
            model_id = int.from_bytes(f.read(4), byteorder='little')

        name, is_mariko = MODEL_MAP.get(model_id, (f"Unknown (ID={model_id})", True))
        if log_func:
            log_func(f"   Console model: {name}")
        return name, is_mariko

    except Exception as e:
        if log_func:
            log_func(f"ERROR reading PRODINFO: {e}")
        return None, None


def find_backup_ids(sd_drive, log_func=None):
    """
    Scan sd:/backup/ for folders with 32-char hex IDs (Hekate backup folders).
    Returns a list of (id_string, full_path) tuples.
    """
    backup_root = os.path.join(sd_drive, "backup")
    if not os.path.isdir(backup_root):
        if log_func:
            log_func("   No 'backup' folder found on SD card.")
        return []

    results = []
    try:
        for entry in os.listdir(backup_root):
            entry_path = os.path.join(backup_root, entry)
            # Hekate backup IDs are hex strings (e.g. "73a88e0b"), length varies
            if os.path.isdir(entry_path) and len(entry) >= 8:
                try:
                    int(entry, 16)  # Validate it's a hex string
                except ValueError:
                    continue
                results.append((entry, entry_path))
                if log_func:
                    log_func(f"   Found backup ID: {entry}")
    except Exception as e:
        if log_func:
            log_func(f"ERROR scanning backup folder: {e}")
    return results


def get_restore_path(backup_id_path, is_emummc=False):
    """Build the restore path for a given backup ID folder."""
    restore = os.path.join(backup_id_path, "restore")
    if is_emummc:
        restore = os.path.join(restore, "emummc")
    os.makedirs(restore, exist_ok=True)
    return restore


