"""Windows power plan, timer resolution, core parking, MMCSS, Game Mode optimizer."""
from __future__ import annotations

import ctypes
import logging
import re
from typing import Optional

from mesh_boost.optimizer.base import (
    IS_WINDOWS, OptimizationResult, RollbackManager,
    is_admin, reg_read, reg_write, require_admin, run_cmd, run_powershell,
)

logger = logging.getLogger(__name__)

# Power plan GUIDs
ULTIMATE_PERFORMANCE = "e9a42b02-d5df-448d-aa00-03f14749eb61"
HIGH_PERFORMANCE = "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c"

# powercfg sub-GUIDs
SUB_PROCESSOR = "54533251-82be-4824-96c1-47b60b740d00"
CPMINCORES = "0cc5b647-c1df-4637-891a-dec35c318583"  # Core parking min cores %
CPMINUNPARKCORES = "0cc5b647-c1df-4637-891a-dec35c318584"  # Unparked cores %

# MMCSS registry paths
MMCSS_TASKS = r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks"
MMCSS_GAMES = MMCSS_TASKS + r"\Games"
MMCSS_PROFILE = r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile"

# Game Mode
GAME_MODE_KEY = r"HKCU\Software\Microsoft\GameBar"
GAME_DVR_KEY = r"HKLM\SOFTWARE\Policies\Microsoft\Windows\GameDVR"


class PowerOptimizer:
    """Optimize Windows power settings for maximum performance."""

    def __init__(self):
        self._rollback = RollbackManager("power")
        self._timer_handle = None

    def apply(self, dry_run: bool = False) -> OptimizationResult:
        """Apply all power optimizations. Returns result summary."""
        result = OptimizationResult("power")

        if not IS_WINDOWS:
            result.skip("all", "not Windows")
            return result

        self._apply_power_plan(result, dry_run)
        self._apply_timer_resolution(result, dry_run)
        self._apply_core_parking(result, dry_run)
        self._apply_mmcss(result, dry_run)
        self._apply_game_mode(result, dry_run)
        self._apply_system_profile(result, dry_run)

        return result

    def restore(self) -> OptimizationResult:
        """Restore all original power settings."""
        result = OptimizationResult("power_restore")

        if not IS_WINDOWS:
            return result

        self._restore_timer_resolution(result)
        self._restore_power_plan(result)
        self._restore_core_parking(result)

        self._rollback.clear()
        return result

    # ── Power Plan ────────────────────────────────────────────────────────

    def _apply_power_plan(self, result: OptimizationResult, dry_run: bool):
        """Activate Ultimate Performance or High Performance power plan."""
        # Get current plan
        ok, output = run_cmd(["powercfg", "/getactivescheme"])
        if ok:
            current_guid = _extract_guid(output)
            self._rollback.save_original("power_plan", current_guid)

            if current_guid in (ULTIMATE_PERFORMANCE, HIGH_PERFORMANCE):
                result.skip("power_plan", f"already high-perf ({current_guid[:8]})")
                return

        if dry_run:
            result.skip("power_plan", "dry_run")
            return

        # Try Ultimate Performance first
        ok, _ = run_cmd(["powercfg", "/setactive", ULTIMATE_PERFORMANCE])
        if ok:
            result.ok("power_plan=ultimate_performance")
            return

        # Ultimate Performance may not exist — try to create it
        ok, output = run_cmd(["powercfg", "/duplicatescheme", HIGH_PERFORMANCE])
        if ok:
            new_guid = _extract_guid(output)
            if new_guid:
                run_cmd(["powercfg", "/setactive", new_guid])
                result.ok(f"power_plan=high_performance_clone ({new_guid[:8]})")
                return

        # Fallback: just activate High Performance
        ok, _ = run_cmd(["powercfg", "/setactive", HIGH_PERFORMANCE])
        if ok:
            result.ok("power_plan=high_performance")
        else:
            result.fail("power_plan", "could not set any high-perf plan")

    def _restore_power_plan(self, result: OptimizationResult):
        original = self._rollback.get_original("power_plan")
        if original:
            ok, _ = run_cmd(["powercfg", "/setactive", original])
            if ok:
                result.ok(f"power_plan restored to {original[:8]}")
            else:
                result.fail("power_plan restore")

    # ── Timer Resolution ──────────────────────────────────────────────────

    def _apply_timer_resolution(self, result: OptimizationResult, dry_run: bool):
        """Set system timer resolution to 0.5ms for minimum input latency.

        This is the single biggest latency reduction on Windows. The default
        timer resolution is 15.6ms — reducing to 0.5ms means:
        - Input polling happens 31x more often
        - Sleep(1) actually sleeps ~1ms instead of ~15ms
        - Game loop timing is much more precise
        """
        if dry_run:
            result.skip("timer_resolution", "dry_run")
            return

        try:
            ntdll = ctypes.WinDLL("ntdll")
            current = ctypes.c_ulong()

            # Query current resolution
            ntdll.NtQueryTimerResolution(
                ctypes.byref(ctypes.c_ulong()),  # min (worst)
                ctypes.byref(ctypes.c_ulong()),  # max (best)
                ctypes.byref(current),            # current
            )
            current_ms = current.value / 10000.0
            self._rollback.save_original("timer_resolution", current.value)

            if current_ms <= 0.6:
                result.skip("timer_resolution", f"already {current_ms:.1f}ms")
                return

            # Set to 0.5ms (5000 * 100ns intervals)
            actual = ctypes.c_ulong()
            status = ntdll.NtSetTimerResolution(5000, True, ctypes.byref(actual))
            if status == 0:  # STATUS_SUCCESS
                actual_ms = actual.value / 10000.0
                result.ok(f"timer_resolution={actual_ms:.2f}ms (was {current_ms:.1f}ms)")
            else:
                # Try 1ms as fallback
                status = ntdll.NtSetTimerResolution(10000, True, ctypes.byref(actual))
                if status == 0:
                    actual_ms = actual.value / 10000.0
                    result.ok(f"timer_resolution={actual_ms:.2f}ms (fallback, was {current_ms:.1f}ms)")
                else:
                    result.fail("timer_resolution", f"NtSetTimerResolution returned 0x{status:08x}")

        except Exception as e:
            result.fail("timer_resolution", str(e))

    def _restore_timer_resolution(self, result: OptimizationResult):
        """Restore original timer resolution."""
        try:
            ntdll = ctypes.WinDLL("ntdll")
            actual = ctypes.c_ulong()
            # Passing False disables our resolution request
            ntdll.NtSetTimerResolution(0, False, ctypes.byref(actual))
            result.ok("timer_resolution restored")
        except Exception as e:
            result.fail("timer_resolution restore", str(e))

    # ── Core Parking ──────────────────────────────────────────────────────

    def _apply_core_parking(self, result: OptimizationResult, dry_run: bool):
        """Disable core parking — keep all cores active for lowest latency.

        Core parking puts idle cores into a low-power state. Great for battery,
        terrible for latency — waking a parked core adds 1-5ms to thread scheduling.
        """
        if not require_admin("core_parking"):
            result.skip("core_parking", "not admin")
            return

        if dry_run:
            result.skip("core_parking", "dry_run")
            return

        # Save current core parking value
        ok, output = run_cmd([
            "powercfg", "/query", "SCHEME_CURRENT",
            SUB_PROCESSOR, CPMINCORES,
        ])
        if ok:
            current = _extract_powercfg_value(output)
            if current is not None:
                self._rollback.save_original("core_parking_min", current)

        # Set min cores to 100% (all cores unparked) on AC power
        ok, _ = run_cmd([
            "powercfg", "/SETACVALUEINDEX", "SCHEME_CURRENT",
            SUB_PROCESSOR, CPMINCORES, "100",
        ])
        if ok:
            # Apply changes
            run_cmd(["powercfg", "/SETACTIVE", "SCHEME_CURRENT"])
            result.ok("core_parking=disabled (100% min cores)")
        else:
            result.fail("core_parking", "powercfg failed")

    def _restore_core_parking(self, result: OptimizationResult):
        original = self._rollback.get_original("core_parking_min")
        if original is not None:
            ok, _ = run_cmd([
                "powercfg", "/SETACVALUEINDEX", "SCHEME_CURRENT",
                SUB_PROCESSOR, CPMINCORES, str(original),
            ])
            if ok:
                run_cmd(["powercfg", "/SETACTIVE", "SCHEME_CURRENT"])
                result.ok(f"core_parking restored to {original}%")

    # ── MMCSS (Multimedia Class Scheduler Service) ────────────────────────

    def _apply_mmcss(self, result: OptimizationResult, dry_run: bool):
        """Configure MMCSS to prioritize game threads.

        MMCSS is Windows' built-in thread scheduler for multimedia. By setting
        Games task to high priority, the kernel gives game threads guaranteed
        CPU time and reduced scheduling latency.
        """
        if not require_admin("mmcss"):
            result.skip("mmcss", "not admin")
            return

        if dry_run:
            result.skip("mmcss", "dry_run")
            return

        settings = {
            # Games task — highest priority for game threads
            (MMCSS_GAMES, "Priority"): 6,              # 1-8, 6=high
            (MMCSS_GAMES, "Scheduling Category"): "High",
            (MMCSS_GAMES, "SFIO Priority"): "High",
            (MMCSS_GAMES, "GPU Priority"): 8,
            (MMCSS_GAMES, "Clock Rate"): 10000,         # 1ms
            (MMCSS_GAMES, "Affinity"): 0,               # all cores
            (MMCSS_GAMES, "Background Only"): "False",
            # System profile — reduce background CPU throttling
            (MMCSS_PROFILE, "SystemResponsiveness"): 10,  # Only 10% reserved for background
            (MMCSS_PROFILE, "NetworkThrottlingIndex"): 0xFFFFFFFF,  # Disable network throttling
        }

        applied = 0
        for (key, name), value in settings.items():
            # Save original
            orig = reg_read(key, name)
            self._rollback.save_original(f"mmcss_{name}", orig)

            if reg_write(key, name, value):
                applied += 1
            else:
                result.fail(f"mmcss_{name}")

        if applied > 0:
            result.ok(f"mmcss={applied} settings (Priority=6, SystemResponsiveness=10%)")

    # ── Game Mode ─────────────────────────────────────────────────────────

    def _apply_game_mode(self, result: OptimizationResult, dry_run: bool):
        """Optimize Game Mode and disable Game DVR/captures.

        Game Mode itself is good (gives games priority scheduling), but
        Game DVR (background recording) and Game Bar overlays hurt FPS.
        """
        if dry_run:
            result.skip("game_mode", "dry_run")
            return

        changes = 0

        # Enable Game Mode (it actually helps on modern Windows)
        orig = reg_read(GAME_MODE_KEY, "AutoGameModeEnabled")
        self._rollback.save_original("game_mode", orig)
        if reg_write(GAME_MODE_KEY, "AutoGameModeEnabled", 1):
            changes += 1

        # Disable Game DVR (background recording kills FPS)
        orig = reg_read(GAME_MODE_KEY, "AllowAutoGameMode")
        self._rollback.save_original("auto_game_mode", orig)
        if reg_write(GAME_MODE_KEY, "AllowAutoGameMode", 1):
            changes += 1

        # Disable Game Bar overlay tips
        orig = reg_read(GAME_MODE_KEY, "ShowStartupPanel")
        self._rollback.save_original("game_bar_tips", orig)
        if reg_write(GAME_MODE_KEY, "ShowStartupPanel", 0):
            changes += 1

        # Disable Game DVR via policy (if admin)
        if is_admin():
            orig = reg_read(GAME_DVR_KEY, "AllowGameDVR")
            self._rollback.save_original("game_dvr_policy", orig)
            if reg_write(GAME_DVR_KEY, "AllowGameDVR", 0):
                changes += 1

        if changes:
            result.ok(f"game_mode={changes} settings (Game Mode on, DVR off)")
        else:
            result.fail("game_mode", "no changes applied")

    # ── System Profile ────────────────────────────────────────────────────

    def _apply_system_profile(self, result: OptimizationResult, dry_run: bool):
        """Tune Windows scheduler for foreground application priority."""
        if dry_run:
            result.skip("system_profile", "dry_run")
            return

        # Win32PrioritySeparation: foreground app priority boost
        # 0x26 = short quantum, high foreground boost (best for gaming)
        sched_key = r"HKLM\SYSTEM\CurrentControlSet\Control\PriorityControl"

        if not require_admin("priority_control"):
            result.skip("system_profile", "not admin")
            return

        orig = reg_read(sched_key, "Win32PrioritySeparation")
        self._rollback.save_original("win32_priority_sep", orig)

        if reg_write(sched_key, "Win32PrioritySeparation", 0x26):
            result.ok("system_profile=Win32PrioritySeparation=0x26 (short quantum, high fg boost)")
        else:
            result.fail("system_profile")

    # ── Status ────────────────────────────────────────────────────────────

    def get_status(self) -> dict:
        """Return current power optimization status."""
        status = {"module": "power", "optimized": False, "settings": {}}

        if not IS_WINDOWS:
            return status

        # Check power plan
        ok, output = run_cmd(["powercfg", "/getactivescheme"])
        if ok:
            guid = _extract_guid(output)
            status["settings"]["power_plan"] = {
                "guid": guid,
                "is_high_perf": guid in (ULTIMATE_PERFORMANCE, HIGH_PERFORMANCE),
            }

        # Check timer resolution
        try:
            ntdll = ctypes.WinDLL("ntdll")
            min_res = ctypes.c_ulong()
            max_res = ctypes.c_ulong()
            current = ctypes.c_ulong()
            ntdll.NtQueryTimerResolution(
                ctypes.byref(min_res), ctypes.byref(max_res), ctypes.byref(current)
            )
            status["settings"]["timer_resolution_ms"] = current.value / 10000.0
            status["settings"]["timer_best_possible_ms"] = max_res.value / 10000.0
        except Exception:
            pass

        # Check MMCSS
        sys_resp = reg_read(MMCSS_PROFILE, "SystemResponsiveness")
        if sys_resp is not None:
            status["settings"]["mmcss_system_responsiveness"] = sys_resp

        status["optimized"] = self._rollback.has_saved_state
        return status


# ── Helpers ───────────────────────────────────────────────────────────────

def _extract_guid(text: str) -> Optional[str]:
    """Extract a GUID from powercfg output."""
    m = re.search(r"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})", text, re.I)
    return m.group(1).lower() if m else None


def _extract_powercfg_value(text: str) -> Optional[int]:
    """Extract the current AC value from powercfg /query output."""
    m = re.search(r"Current AC Power Setting Index:\s*0x([0-9a-fA-F]+)", text)
    if m:
        return int(m.group(1), 16)
    return None
