"""
Hardware-based device identification.

Collects a stable hardware UUID that persists across OS reinstalls:
- macOS: IOPlatformUUID from IOKit
- Linux: DMI product UUID from /sys/class/dmi/id/product_uuid
- Windows: MachineGuid from registry

The UUID is hashed with SHA-256 for privacy before sending to server.
"""

import hashlib
import platform
import re
import subprocess

# UUID format regex (8-4-4-4-12 hex digits)
UUID_PATTERN = re.compile(
    r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
)

# Machine-id format: 32 lowercase hex characters (no dashes)
MACHINE_ID_PATTERN = re.compile(r'^[0-9a-f]{32}$')


def _is_valid_uuid(value: str) -> bool:
    """Validate that a string is a properly formatted UUID."""
    return bool(value and UUID_PATTERN.match(value))


def _is_valid_machine_id(value: str) -> bool:
    """Validate that a string is a properly formatted machine-id (32 hex chars)."""
    return bool(value and MACHINE_ID_PATTERN.match(value))


def get_hardware_uuid() -> str | None:
    """Get the raw hardware UUID for this machine."""
    system = platform.system()

    try:
        if system == "Darwin":  # macOS
            return _get_macos_uuid()
        elif system == "Linux":
            return _get_linux_uuid()
        elif system == "Windows":
            return _get_windows_uuid()
        else:
            return None
    except Exception:
        return None


def _get_macos_uuid() -> str | None:
    """Get IOPlatformUUID on macOS."""
    try:
        result = subprocess.run(
            ["ioreg", "-rd1", "-c", "IOPlatformExpertDevice"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        for line in result.stdout.split("\n"):
            if "IOPlatformUUID" in line:
                # Line format: "IOPlatformUUID" = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
                uuid = line.split('"')[-2]
                if _is_valid_uuid(uuid):
                    return uuid
    except Exception:
        pass
    return None


def _get_linux_uuid() -> str | None:
    """Get DMI product UUID on Linux."""
    # Try /sys/class/dmi/id/product_uuid (requires root)
    try:
        with open("/sys/class/dmi/id/product_uuid", "r") as f:
            uuid = f.read().strip()
            if _is_valid_uuid(uuid):
                return uuid
    except (PermissionError, FileNotFoundError):
        pass

    # Fallback to /etc/machine-id (less stable but available without root)
    # This is the most common fallback and doesn't require elevated privileges
    # Note: machine-id is a 32-char hex string, not a UUID, but it's stable
    try:
        with open("/etc/machine-id", "r") as f:
            machine_id = f.read().strip()
            # machine-id is 32 lowercase hex characters (no dashes)
            if _is_valid_machine_id(machine_id):
                return machine_id
    except FileNotFoundError:
        pass

    # Note: We intentionally do NOT use sudo dmidecode here as it:
    # 1. Could block waiting for password input
    # 2. Would require user interaction
    # 3. /etc/machine-id is sufficient for our use case

    return None


def _get_windows_uuid() -> str | None:
    """Get MachineGuid on Windows."""
    try:
        import winreg

        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\Microsoft\Cryptography",
            0,
            winreg.KEY_READ | winreg.KEY_WOW64_64KEY,
        )
        value, _ = winreg.QueryValueEx(key, "MachineGuid")
        winreg.CloseKey(key)
        # MachineGuid is a UUID format string
        if _is_valid_uuid(value):
            return value
    except Exception:
        pass

    # Fallback: try PowerShell Get-CimInstance (replaces deprecated wmic)
    try:
        result = subprocess.run(
            ["powershell", "-NoProfile", "-Command",
             "(Get-CimInstance -ClassName Win32_ComputerSystemProduct).UUID"],
            capture_output=True,
            text=True,
            timeout=10,
        )
        uuid = result.stdout.strip()
        if _is_valid_uuid(uuid):
            return uuid
    except Exception:
        pass

    return None


def get_device_id() -> str | None:
    """
    Get a hashed device identifier.

    Returns SHA-256 hash of the hardware UUID, or None if unavailable.
    The hash provides privacy while maintaining uniqueness.
    """
    uuid = get_hardware_uuid()
    if not uuid:
        return None

    # Hash for privacy
    return hashlib.sha256(uuid.encode()).hexdigest()
