"""Game and application auto-detection for Windows.

Scans running processes and known install paths to detect games and
performance-sensitive applications. Supports Steam, Epic, Xbox, and
standalone installs.
"""
from __future__ import annotations

import json
import logging
import os
from pathlib import Path
from typing import Optional

from mesh_boost.optimizer.base import IS_WINDOWS, IS_LINUX, run_powershell, reg_read

logger = logging.getLogger(__name__)

# Known game executables — maps exe name to game info
# This is the built-in database; user profiles can extend it
_KNOWN_GAMES: dict[str, dict] = {
    # FPS / Competitive
    "valorant.exe": {"name": "Valorant", "genre": "fps", "publisher": "Riot Games"},
    "csgo.exe": {"name": "CS:GO", "genre": "fps", "publisher": "Valve"},
    "cs2.exe": {"name": "Counter-Strike 2", "genre": "fps", "publisher": "Valve"},
    "overwatch.exe": {"name": "Overwatch 2", "genre": "fps", "publisher": "Blizzard"},
    "r5apex.exe": {"name": "Apex Legends", "genre": "fps", "publisher": "Respawn"},
    "fortniteclient-win64-shipping.exe": {"name": "Fortnite", "genre": "fps", "publisher": "Epic Games"},
    "cod.exe": {"name": "Call of Duty", "genre": "fps", "publisher": "Activision"},
    "destiny2.exe": {"name": "Destiny 2", "genre": "fps", "publisher": "Bungie"},
    "rainbowsix.exe": {"name": "Rainbow Six Siege", "genre": "fps", "publisher": "Ubisoft"},
    "pubg.exe": {"name": "PUBG", "genre": "fps", "publisher": "PUBG Corp"},
    "tarkov.exe": {"name": "Escape from Tarkov", "genre": "fps", "publisher": "BSG"},
    "bf2042.exe": {"name": "Battlefield 2042", "genre": "fps", "publisher": "EA"},
    "halo infinite.exe": {"name": "Halo Infinite", "genre": "fps", "publisher": "343 Industries"},
    "mw2.exe": {"name": "MW2", "genre": "fps", "publisher": "Activision"},

    # Battle Royale / Survival
    "rustclient.exe": {"name": "Rust", "genre": "survival", "publisher": "Facepunch"},
    "dayz_x64.exe": {"name": "DayZ", "genre": "survival", "publisher": "Bohemia"},
    "arkascended.exe": {"name": "ARK: Survival Ascended", "genre": "survival", "publisher": "Studio Wildcard"},

    # MOBA / RTS
    "league of legends.exe": {"name": "League of Legends", "genre": "moba", "publisher": "Riot Games"},
    "dota2.exe": {"name": "Dota 2", "genre": "moba", "publisher": "Valve"},

    # Racing / Sim
    "forzahorizon5.exe": {"name": "Forza Horizon 5", "genre": "racing", "publisher": "Microsoft"},
    "iracing.exe": {"name": "iRacing", "genre": "racing", "publisher": "iRacing"},
    "acc.exe": {"name": "Assetto Corsa Competizione", "genre": "racing", "publisher": "Kunos"},

    # RPG / Open World
    "cyberpunk2077.exe": {"name": "Cyberpunk 2077", "genre": "rpg", "publisher": "CDPR"},
    "eldenring.exe": {"name": "Elden Ring", "genre": "rpg", "publisher": "FromSoftware"},
    "hogwartslegacy.exe": {"name": "Hogwarts Legacy", "genre": "rpg", "publisher": "WB Games"},
    "bg3.exe": {"name": "Baldur's Gate 3", "genre": "rpg", "publisher": "Larian"},
    "starfield.exe": {"name": "Starfield", "genre": "rpg", "publisher": "Bethesda"},
    "gta5.exe": {"name": "GTA V", "genre": "openworld", "publisher": "Rockstar"},

    # MMO
    "ffxiv_dx11.exe": {"name": "Final Fantasy XIV", "genre": "mmo", "publisher": "Square Enix"},
    "wow.exe": {"name": "World of Warcraft", "genre": "mmo", "publisher": "Blizzard"},
    "newworld.exe": {"name": "New World", "genre": "mmo", "publisher": "Amazon"},
    "gw2-64.exe": {"name": "Guild Wars 2", "genre": "mmo", "publisher": "ArenaNet"},

    # Sandbox / Creative
    "minecraft.exe": {"name": "Minecraft", "genre": "sandbox", "publisher": "Mojang"},
    "javaw.exe": {"name": "Minecraft (Java)", "genre": "sandbox", "publisher": "Mojang"},

    # Streaming / Content
    "obs64.exe": {"name": "OBS Studio", "genre": "streaming", "publisher": "OBS Project"},
    "streamlabs obs.exe": {"name": "Streamlabs", "genre": "streaming", "publisher": "Streamlabs"},

    # Launchers (detect these to know games may launch soon)
    "steam.exe": {"name": "Steam", "genre": "launcher", "publisher": "Valve"},
    "epicgameslauncher.exe": {"name": "Epic Games Launcher", "genre": "launcher", "publisher": "Epic"},
    "battlenet.exe": {"name": "Battle.net", "genre": "launcher", "publisher": "Blizzard"},
    "eadesktop.exe": {"name": "EA Desktop", "genre": "launcher", "publisher": "EA"},
    "galaxyclient.exe": {"name": "GOG Galaxy", "genre": "launcher", "publisher": "GOG"},
    "ubisoftconnect.exe": {"name": "Ubisoft Connect", "genre": "launcher", "publisher": "Ubisoft"},
}

# Linux-native and Proton game process names
_KNOWN_GAMES_LINUX: dict[str, dict] = {
    # Native Linux
    "cs2": {"name": "Counter-Strike 2", "genre": "fps", "publisher": "Valve"},
    "csgo_linux64": {"name": "CS:GO", "genre": "fps", "publisher": "Valve"},
    "dota2": {"name": "Dota 2", "genre": "moba", "publisher": "Valve"},
    "hl2_linux": {"name": "Half-Life 2", "genre": "fps", "publisher": "Valve"},
    "tf_linux64": {"name": "Team Fortress 2", "genre": "fps", "publisher": "Valve"},
    "factorio": {"name": "Factorio", "genre": "sandbox", "publisher": "Wube"},
    "terraria": {"name": "Terraria", "genre": "sandbox", "publisher": "Re-Logic"},
    "RustClient": {"name": "Rust", "genre": "survival", "publisher": "Facepunch"},
    "valheim.x86_64": {"name": "Valheim", "genre": "survival", "publisher": "Iron Gate"},
    "Minecraft": {"name": "Minecraft", "genre": "sandbox", "publisher": "Mojang"},
    "java": {"name": "Minecraft (Java)", "genre": "sandbox", "publisher": "Mojang"},
    "obs": {"name": "OBS Studio", "genre": "streaming", "publisher": "OBS Project"},
    "obs-studio": {"name": "OBS Studio", "genre": "streaming", "publisher": "OBS Project"},
    # Proton/Wine (shows as .exe in process list)
    "wine64-preloader": {"name": "Proton Game", "genre": "proton", "publisher": "Various"},
    "wineserver": {"name": "Wine Server", "genre": "launcher", "publisher": "Wine"},
    # Lutris/Steam
    "steam": {"name": "Steam", "genre": "launcher", "publisher": "Valve"},
    "lutris": {"name": "Lutris", "genre": "launcher", "publisher": "Lutris"},
    "heroic": {"name": "Heroic Launcher", "genre": "launcher", "publisher": "Heroic"},
}

# Merge Linux games into main db at import time
_KNOWN_GAMES.update(_KNOWN_GAMES_LINUX)

# Common game install directories
_GAME_DIRS_WINDOWS = [
    r"C:\Program Files (x86)\Steam\steamapps\common",
    r"C:\Program Files\Epic Games",
    r"C:\Program Files\Riot Games",
    r"C:\Program Files (x86)\Riot Games",
    r"C:\Program Files\Xbox Games",
    r"D:\SteamLibrary\steamapps\common",
    r"E:\SteamLibrary\steamapps\common",
]

_GAME_DIRS_LINUX = [
    os.path.expanduser("~/.steam/steam/steamapps/common"),
    os.path.expanduser("~/.local/share/Steam/steamapps/common"),
    os.path.expanduser("~/.local/share/lutris/runners"),
    "/opt/games",
]


class GameDetector:
    """Detect running games and installed games on Windows and Linux."""

    def __init__(self, custom_db_path: Optional[Path] = None):
        self._games = dict(_KNOWN_GAMES)
        self._custom_db = custom_db_path
        if custom_db_path and custom_db_path.exists():
            self._load_custom_db(custom_db_path)

    def detect_running(self) -> list[dict]:
        """Scan running processes for known games.

        Returns list of dicts with: name, exe, pid, genre, publisher
        """
        if not IS_WINDOWS and not IS_LINUX:
            return []

        try:
            import psutil
            running = []
            for proc in psutil.process_iter(['pid', 'name', 'exe']):
                try:
                    pname = proc.info['name']
                    if not pname:
                        continue
                    pname_lower = pname.lower()
                    if pname_lower in self._games:
                        info = self._games[pname_lower].copy()
                        info['pid'] = proc.info['pid']
                        info['exe'] = proc.info['exe'] or pname
                        info['process_name'] = pname
                        running.append(info)
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue
            return running
        except ImportError:
            logger.warning("psutil not available — falling back to WMI")
            return self._detect_via_wmi()

    def detect_running_simple(self) -> list[dict]:
        """Lightweight detection without psutil dependency."""
        from mesh_boost.optimizer.base import run_cmd

        if IS_WINDOWS:
            ok, output = run_cmd(["tasklist", "/FO", "CSV", "/NH"])
            if not ok:
                return []
            running = []
            for line in output.splitlines():
                parts = line.strip().strip('"').split('","')
                if len(parts) < 2:
                    continue
                pname = parts[0].lower()
                if pname in self._games:
                    info = self._games[pname].copy()
                    try:
                        info['pid'] = int(parts[1])
                    except (ValueError, IndexError):
                        info['pid'] = 0
                    info['process_name'] = parts[0]
                    running.append(info)
            return running

        elif IS_LINUX:
            # Use /proc directly — no external deps needed
            running = []
            proc_dir = Path("/proc")
            for entry in proc_dir.iterdir():
                if not entry.name.isdigit():
                    continue
                try:
                    comm = (entry / "comm").read_text().strip()
                    # Check both with and without .exe
                    for check in [comm.lower(), comm.lower() + ".exe", comm]:
                        if check in self._games:
                            info = self._games[check].copy()
                            info['pid'] = int(entry.name)
                            info['process_name'] = comm
                            running.append(info)
                            break
                except (PermissionError, FileNotFoundError):
                    continue
            return running

        return []

    def is_game_running(self) -> bool:
        """Quick check: is any known game currently running?"""
        games = self.detect_running_simple()
        return any(g.get("genre") != "launcher" for g in games)

    def get_running_game(self) -> Optional[dict]:
        """Get the primary running game (first non-launcher found)."""
        games = self.detect_running()
        for g in games:
            if g.get("genre") != "launcher":
                return g
        return None

    def get_steam_games(self) -> list[str]:
        """Get list of installed Steam games."""
        steam_paths = []

        if IS_WINDOWS:
            steam_paths = [
                Path(r"C:\Program Files (x86)\Steam"),
                Path(r"D:\SteamLibrary"),
                Path(r"E:\SteamLibrary"),
            ]
            steam_reg = reg_read(r"HKCU\Software\Valve\Steam", "SteamPath")
            if steam_reg:
                steam_paths.insert(0, Path(steam_reg))
        elif IS_LINUX:
            steam_paths = [
                Path.home() / ".steam" / "steam",
                Path.home() / ".local" / "share" / "Steam",
            ]

        games = []
        for sp in steam_paths:
            common = sp / "steamapps" / "common"
            if common.exists():
                for d in common.iterdir():
                    if d.is_dir():
                        games.append(d.name)
        return games

    def get_installed_launchers(self) -> list[str]:
        """Detect which game launchers are installed."""
        launchers = []

        checks = [
            ("Steam", r"C:\Program Files (x86)\Steam\steam.exe"),
            ("Epic Games", r"C:\Program Files (x86)\Epic Games\Launcher\Portal\Binaries\Win64\EpicGamesLauncher.exe"),
            ("Battle.net", r"C:\Program Files (x86)\Battle.net\Battle.net.exe"),
            ("EA Desktop", r"C:\Program Files\Electronic Arts\EA Desktop\EA Desktop\EADesktop.exe"),
            ("Riot Games", r"C:\Riot Games\Riot Client\RiotClientServices.exe"),
            ("Xbox", r"C:\Program Files\WindowsApps"),  # Xbox Game Pass
            ("GOG Galaxy", r"C:\Program Files (x86)\GOG Galaxy\GalaxyClient.exe"),
            ("Ubisoft Connect", r"C:\Program Files (x86)\Ubisoft\Ubisoft Game Launcher\UbisoftConnect.exe"),
        ]

        for name, path in checks:
            if Path(path).exists():
                launchers.append(name)

        return launchers

    def _detect_via_wmi(self) -> list[dict]:
        """Fallback: detect games via WMI/PowerShell."""
        ok, output = run_powershell(
            "Get-Process | Select-Object Id, ProcessName | ConvertTo-Json"
        )
        if not ok:
            return []

        running = []
        try:
            processes = json.loads(output)
            if isinstance(processes, dict):
                processes = [processes]
            for proc in processes:
                pname = (proc.get("ProcessName", "") + ".exe").lower()
                if pname in self._games:
                    info = self._games[pname].copy()
                    info['pid'] = proc.get("Id", 0)
                    info['process_name'] = proc.get("ProcessName", "")
                    running.append(info)
        except json.JSONDecodeError:
            pass
        return running

    def _load_custom_db(self, path: Path):
        """Load user-defined game database."""
        try:
            data = json.loads(path.read_text())
            for exe, info in data.items():
                self._games[exe.lower()] = info
            logger.info("Loaded %d custom game entries from %s", len(data), path)
        except Exception as e:
            logger.warning("Failed to load custom game DB: %s", e)

    def add_game(self, exe_name: str, name: str, genre: str = "unknown",
                 publisher: str = "unknown"):
        """Add a game to the detector at runtime."""
        self._games[exe_name.lower()] = {
            "name": name, "genre": genre, "publisher": publisher,
        }

    def get_known_games(self) -> dict[str, dict]:
        """Return the full game database."""
        return dict(self._games)
