"""Profile manager — load, match, and apply per-game profiles."""
from __future__ import annotations

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

from mesh_boost.optimizer.base import (
    IS_LINUX, IS_WINDOWS, is_admin, RollbackManager,
)
from mesh_boost.profiles.models import GameProfile, BUILTIN_PROFILES

logger = logging.getLogger(__name__)

_CUSTOM_PROFILES_DIR = Path.home() / ".mesh-optimizer" / "boost_profiles"


class ProfileManager:
    """Manage and apply per-game optimization profiles."""

    def __init__(self):
        self._profiles: dict[str, GameProfile] = dict(BUILTIN_PROFILES)
        self._active_profile: Optional[str] = None
        self._rollback = RollbackManager("profiles")
        self._load_custom_profiles()

    def match_game(self, exe_name: str, genre: str = None) -> Optional[GameProfile]:
        """Find the best matching profile for a game executable.

        1. Exact exe match from named profiles
        2. Genre default profile
        3. None
        """
        exe_lower = exe_name.lower()

        # Check all profiles for exe match
        for key, profile in self._profiles.items():
            if key.startswith("_"):
                continue  # skip genre defaults
            for pexe in profile.exe_names:
                if pexe.lower() == exe_lower:
                    return profile

        # Fall back to genre default
        if genre:
            genre_key = f"_{genre}_default"
            if genre_key in self._profiles:
                return self._profiles[genre_key]

        return None

    def apply_profile(self, profile: GameProfile, pid: int = None,
                      tier: str = "gamer") -> dict:
        """Apply a game profile's optimizations.

        Args:
            profile: The profile to apply
            pid: Optional PID of the game process to boost
            tier: User's license tier (gates pro-only profiles)

        Returns:
            Summary dict of what was applied
        """
        if profile.tier == "pro" and tier != "pro":
            return {"error": f"Profile '{profile.name}' requires Pro tier"}

        results = {"profile": profile.name, "applied": [], "skipped": [], "failed": []}

        # Apply process priority if we have a PID
        if pid and profile.process.priority:
            self._apply_process_boost(pid, profile, results)

        # Apply env vars (for Proton/Wine games on Linux)
        if IS_LINUX and profile.env_vars:
            import os
            for key, value in profile.env_vars.items():
                orig = os.environ.get(key)
                self._rollback.save_original(f"env_{key}", orig)
                os.environ[key] = value
                results["applied"].append(f"env {key}={value}")

        self._active_profile = profile.name
        logger.info("Profile applied: %s (%d applied, %d skipped)",
                     profile.name, len(results["applied"]), len(results["skipped"]))
        return results

    def restore_profile(self) -> dict:
        """Restore settings from the active profile."""
        if not self._active_profile:
            return {"status": "no_active_profile"}

        results = {"profile": self._active_profile, "restored": []}

        # Restore env vars
        if IS_LINUX:
            import os
            for key in list(self._rollback._originals.keys()):
                if key.startswith("env_"):
                    env_key = key[4:]
                    orig = self._rollback.get_original(key)
                    if orig is not None:
                        os.environ[env_key] = orig
                    elif env_key in os.environ:
                        del os.environ[env_key]
                    results["restored"].append(f"env {env_key}")

        # Restore background process priorities
        if IS_LINUX:
            from mesh_boost.optimizer.linux_process import LinuxProcessOptimizer
            LinuxProcessOptimizer().restore_background()
        elif IS_WINDOWS:
            from mesh_boost.optimizer.process import ProcessOptimizer
            ProcessOptimizer().restore_background()

        self._rollback.clear()
        old_profile = self._active_profile
        self._active_profile = None
        logger.info("Profile restored: %s", old_profile)
        return results

    def get_profile(self, name: str) -> Optional[GameProfile]:
        """Get a profile by name."""
        return self._profiles.get(name)

    def list_profiles(self) -> list[dict]:
        """List all available profiles (excluding genre defaults)."""
        return [
            {
                "key": key,
                "name": p.name,
                "genre": p.genre,
                "tier": p.tier,
                "exe_names": p.exe_names,
            }
            for key, p in sorted(self._profiles.items())
            if not key.startswith("_")
        ]

    @property
    def active_profile(self) -> Optional[str]:
        return self._active_profile

    def add_custom_profile(self, key: str, profile: GameProfile):
        """Add or update a custom profile and save to disk."""
        self._profiles[key] = profile
        self._save_custom_profile(key, profile)

    # ── Internal ─────────────────────────────────────────────────────────

    def _apply_process_boost(self, pid: int, profile: GameProfile, results: dict):
        """Apply process-level optimizations from profile."""
        if IS_LINUX:
            from mesh_boost.optimizer.linux_process import LinuxProcessOptimizer
            proc = LinuxProcessOptimizer()
            r = proc.boost_process(pid, priority=profile.process.priority or "high")
            if "nice" in r:
                results["applied"].append(f"nice={r['nice']}")
            if "ionice" in r:
                results["applied"].append(f"ionice={r['ionice']}")
            if "scheduler" in r:
                results["applied"].append(f"sched={r['scheduler']}")

            if profile.process.throttle_background:
                throttled = proc.throttle_background(pid)
                if throttled:
                    results["applied"].append(f"throttled {len(throttled)} bg processes")

        elif IS_WINDOWS:
            from mesh_boost.optimizer.process import ProcessOptimizer
            proc = ProcessOptimizer()
            r = proc.boost_process(pid, priority=profile.process.priority or "high")
            if "priority" in r:
                results["applied"].append(f"priority={r['priority']}")
            if "affinity" in r:
                results["applied"].append(f"affinity={r['affinity']}")

            if profile.process.throttle_background:
                throttled = proc.throttle_background(pid)
                if throttled:
                    results["applied"].append(f"throttled {len(throttled)} bg processes")

    def _load_custom_profiles(self):
        """Load user-defined profiles from disk."""
        if not _CUSTOM_PROFILES_DIR.exists():
            return

        for f in _CUSTOM_PROFILES_DIR.glob("*.json"):
            try:
                data = json.loads(f.read_text())
                profile = GameProfile(**data)
                key = f.stem
                self._profiles[key] = profile
                logger.debug("Loaded custom profile: %s", key)
            except Exception as e:
                logger.warning("Failed to load profile %s: %s", f.name, e)

    def _save_custom_profile(self, key: str, profile: GameProfile):
        """Save a custom profile to disk."""
        _CUSTOM_PROFILES_DIR.mkdir(parents=True, exist_ok=True)
        path = _CUSTOM_PROFILES_DIR / f"{key}.json"
        path.write_text(profile.model_dump_json(indent=2))
