# =============================================================================
#  installer/src/sysconfig.py — Windows system configuration helpers
# =============================================================================
#
#  All functions target Windows 10/11 (tested on J6412 with Windows 10 IoT).
#  Requires the installer to be run as Administrator for firewall and registry
#  operations; shortcuts and HKCU run-key work without elevation.
# =============================================================================

from __future__ import annotations

import ctypes
import os
import subprocess
import sys
import winreg
from pathlib import Path

# ---------------------------------------------------------------------------
#  Install paths
# ---------------------------------------------------------------------------

INSTALL_ROOT  = Path(os.environ.get("ProgramFiles", "C:\\Program Files")) / "AR Electronics"
ECDIS_DIR     = INSTALL_ROOT / "AR-ECDIS"
AUTOPILOT_DIR = INSTALL_ROOT / "AR-Autopilot"
APPDATA_DIR   = Path.home() / "AppData" / "Roaming" / "AR Electronics"
START_MENU    = Path(os.environ.get("APPDATA", "")) / "Microsoft" / "Windows" / "Start Menu" / "Programs" / "AR Electronics"


def ensure_install_dirs() -> None:
    """Create install directory tree (requires admin)."""
    for d in (INSTALL_ROOT, ECDIS_DIR, AUTOPILOT_DIR, APPDATA_DIR, START_MENU):
        d.mkdir(parents=True, exist_ok=True)


# ---------------------------------------------------------------------------
#  Auto-start (HKCU — no admin required)
# ---------------------------------------------------------------------------

_AUTORUN_KEY = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
_AR_ECDIS_VALUE    = "AR-ECDIS"
_AR_AUTOPILOT_VALUE = "AR-Autopilot"


def register_autostart(name: str, exe_path: Path, args: str = "") -> None:
    """
    Add an entry to HKCU Run so the app starts with Windows.

    Does NOT require administrator rights (current-user key).
    """
    cmd = f'"{exe_path}" {args}'.strip()
    key = winreg.OpenKey(
        winreg.HKEY_CURRENT_USER,
        _AUTORUN_KEY,
        access=winreg.KEY_SET_VALUE,
    )
    winreg.SetValueEx(key, name, 0, winreg.REG_SZ, cmd)
    winreg.CloseKey(key)


def unregister_autostart(name: str) -> None:
    """Remove an auto-start entry (best-effort, ignores missing keys)."""
    try:
        key = winreg.OpenKey(
            winreg.HKEY_CURRENT_USER,
            _AUTORUN_KEY,
            access=winreg.KEY_SET_VALUE,
        )
        winreg.DeleteValue(key, name)
        winreg.CloseKey(key)
    except FileNotFoundError:
        pass


def configure_autostart() -> None:
    """Register both AR Electronics apps in the Windows autostart."""
    ecdis_exe     = ECDIS_DIR     / "AR-ECDIS.exe"
    autopilot_exe = AUTOPILOT_DIR / "ar_autopilot_display.exe"

    if ecdis_exe.exists():
        register_autostart(_AR_ECDIS_VALUE, ecdis_exe)

    if autopilot_exe.exists():
        register_autostart(_AR_AUTOPILOT_VALUE, autopilot_exe)


# ---------------------------------------------------------------------------
#  Desktop and Start Menu shortcuts
# ---------------------------------------------------------------------------

def _make_shortcut(target: Path, shortcut_path: Path, icon: Path | None = None) -> None:
    """
    Create a .lnk shortcut using PowerShell WScript.Shell.

    No admin required; works on standard user accounts.
    """
    icon_str = f'$s.IconLocation = "{icon}"' if icon else ""
    script = (
        f'$s=(New-Object -COM WScript.Shell).CreateShortcut("{shortcut_path}");'
        f'$s.TargetPath="{target}";'
        f'{icon_str};'
        f'$s.Save()'
    )
    subprocess.run(["powershell", "-Command", script], check=True, capture_output=True)


def create_shortcuts() -> None:
    """Create Start Menu and Desktop shortcuts for both apps."""
    START_MENU.mkdir(parents=True, exist_ok=True)
    desktop = Path.home() / "Desktop"

    apps = [
        ("AR-ECDIS",       ECDIS_DIR     / "AR-ECDIS.exe"),
        ("AR-Autopilot",   AUTOPILOT_DIR / "ar_autopilot_display.exe"),
    ]
    for name, exe in apps:
        if not exe.exists():
            continue
        lnk = f"{name}.lnk"
        _make_shortcut(exe, START_MENU / lnk, icon=exe)
        _make_shortcut(exe, desktop / lnk,    icon=exe)


# ---------------------------------------------------------------------------
#  Windows Firewall rules (requires admin)
# ---------------------------------------------------------------------------

def add_firewall_rules() -> None:
    """
    Allow inbound TCP on ports used by AR Electronics services.

    Requires the installer to be running as Administrator.
    """
    rules = [
        ("AR-ECDIS Web",      "TCP", "8080"),   # AR-ECDIS FastAPI backend
        ("AR-ECDIS WebSocket","TCP", "8080"),
    ]
    for name, proto, port in rules:
        subprocess.run(
            [
                "netsh", "advfirewall", "firewall", "add", "rule",
                f"name={name}",
                "dir=in",
                "action=allow",
                f"protocol={proto}",
                f"localport={port}",
            ],
            check=False,        # non-fatal if rule already exists
            capture_output=True,
        )


# ---------------------------------------------------------------------------
#  COM port detection (informational — no forced assignment)
# ---------------------------------------------------------------------------

def list_com_ports() -> list[str]:
    """
    Return list of detected COM ports on the system.

    On Windows this queries the registry rather than requiring PySerial.
    """
    ports: list[str] = []
    try:
        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"HARDWARE\DEVICEMAP\SERIALCOMM",
        )
        i = 0
        while True:
            try:
                _, port_name, _ = winreg.EnumValue(key, i)
                ports.append(port_name)
                i += 1
            except OSError:
                break
        winreg.CloseKey(key)
    except OSError:
        pass
    return sorted(ports)


# ---------------------------------------------------------------------------
#  Administrator check
# ---------------------------------------------------------------------------

def is_admin() -> bool:
    """Return True if the current process has administrator privileges."""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin() != 0
    except AttributeError:
        return False


def require_admin() -> None:
    """Re-launch this process with UAC elevation if not already admin."""
    if not is_admin():
        ctypes.windll.shell32.ShellExecuteW(
            None, "runas", sys.executable, " ".join(sys.argv), None, 1
        )
        sys.exit(0)
