"""
Optimizer module - uses the Prolog engine to generate and apply optimizations
"""

import platform
import subprocess
import os
from typing import Dict, List, Optional, Tuple

from .detector import detect_system, get_system_facts
from .reasoner import PrologEngine, fact, var
from .knowledge import build_knowledge_base, TASKS


def create_engine() -> PrologEngine:
    """Create and initialize the Prolog engine with system facts and rules"""
    engine = PrologEngine()
    system_facts = get_system_facts()
    build_knowledge_base(engine, system_facts)
    return engine


def list_tasks() -> Dict[str, str]:
    """List all available optimization tasks"""
    return {name: info["description"] for name, info in TASKS.items()}


def get_suitability(task: str) -> Optional[Tuple[str, str]]:
    """
    Check system suitability for a task
    Returns (task, rating) or None if not suitable
    """
    engine = create_engine()

    # Query for suitability
    Rating = var("Rating")
    solutions = engine.query_all(fact("suitable_for", task, Rating))

    if solutions:
        # Return highest rating found
        ratings_order = ["excellent", "good", "limited", "cpu_only"]
        best_rating = None
        for sol in solutions:
            rating = sol.get("Rating")
            if rating and (best_rating is None or ratings_order.index(rating) < ratings_order.index(best_rating)):
                best_rating = rating
        return (task, best_rating) if best_rating else None

    return None


def get_recommendations(task: str) -> Dict:
    """
    Get optimization recommendations for a task
    Returns dict with suitability, optimizations, and warnings
    """
    engine = create_engine()
    system = detect_system()

    result = {
        "task": task,
        "task_description": TASKS.get(task, {}).get("description", "Unknown task"),
        "system_summary": {
            "cpu": f"{system['cpu']['cores']} cores ({system['cpu']['tier']})",
            "ram": f"{system['ram']['total_gb']} GB ({system['ram']['tier']})",
            "gpu": f"{system['gpu']['vendor']} {system['gpu']['model']} ({system['gpu']['tier']})",
            "disk": f"{system['disk']['type'].upper()} ({system['disk']['tier']})",
        },
        "suitability": None,
        "optimizations": [],
        "warnings": [],
        "can_optimize": True,
    }

    # Check suitability
    Rating = var("Rating")
    suitability_solutions = engine.query_all(fact("suitable_for", task, Rating))
    if suitability_solutions:
        ratings = [sol.get("Rating") for sol in suitability_solutions if sol.get("Rating")]
        ratings_order = ["excellent", "good", "limited", "cpu_only"]
        if ratings:
            best = min(ratings, key=lambda r: ratings_order.index(r) if r in ratings_order else 99)
            result["suitability"] = best

    # Get optimizations
    Setting = var("Setting")
    Value = var("Value")
    opt_solutions = engine.query_all(fact("optimize", task, Setting, Value))

    seen = set()
    for sol in opt_solutions:
        setting = sol.get("Setting")
        value = sol.get("Value")
        if setting and (setting, value) not in seen:
            seen.add((setting, value))
            result["optimizations"].append({
                "setting": setting,
                "value": value,
                "description": get_setting_description(setting, value),
            })

    # Get warnings
    WarnType = var("WarnType")
    Message = var("Message")
    warn_solutions = engine.query_all(fact("warning", task, WarnType, Message))

    seen_warnings = set()
    for sol in warn_solutions:
        warn_type = sol.get("WarnType")
        message = sol.get("Message")
        if message and message not in seen_warnings:
            seen_warnings.add(message)
            result["warnings"].append({
                "type": warn_type,
                "message": message,
            })

    # Determine if we can optimize
    if not result["optimizations"]:
        result["can_optimize"] = False

    return result


def get_setting_description(setting: str, value) -> str:
    """Get human-readable description of a setting"""
    descriptions = {
        "set_power_mode": {
            "high_performance": "Set power plan to High Performance for maximum speed",
            "balanced": "Set power plan to Balanced for efficiency",
            "power_saver": "Set power plan to Power Saver to conserve battery",
        },
        "set_gpu_mode": {
            "performance": "Set GPU to prefer maximum performance",
            "power_saving": "Set GPU to prefer power saving",
        },
        "disable_background_apps": {
            True: "Disable unnecessary background applications",
        },
        "set_process_priority": {
            "high": "Set application priority to High",
            "normal": "Set application priority to Normal",
        },
        "allocate_ram_percent": "Allocate {}% of RAM to application".format(value),
        "enable_gpu_acceleration": {
            True: "Enable GPU hardware acceleration",
        },
        "set_cuda_visible_devices": "Set CUDA_VISIBLE_DEVICES={}".format(value),
        "enable_file_indexing": {
            True: "Enable file system indexing for faster searches",
        },
        "set_make_jobs": "Set parallel make jobs to {} (-j{})".format(value, value),
        "reduce_brightness": "Reduce screen brightness to {}%".format(value),
        "limit_cpu_percent": "Limit CPU usage to {}%".format(value),
        "set_fan_mode": {
            "quiet": "Set fan profile to quiet mode",
            "performance": "Set fan profile to performance mode",
        },
    }

    if setting in descriptions:
        desc = descriptions[setting]
        if isinstance(desc, dict):
            return desc.get(value, f"Set {setting} to {value}")
        return desc

    return f"Set {setting} to {value}"


def optimize_for(task: str, dry_run: bool = True) -> Dict:
    """
    Optimize the system for a specific task

    Args:
        task: The task to optimize for
        dry_run: If True, only show what would be done (default)

    Returns:
        Dict with recommendations and actions taken
    """
    recommendations = get_recommendations(task)

    if dry_run:
        recommendations["mode"] = "dry_run"
        recommendations["message"] = "Dry run - no changes made. Use --apply to apply optimizations."
        return recommendations

    # Actually apply optimizations
    system = platform.system().lower()
    applied = []
    failed = []

    for opt in recommendations["optimizations"]:
        setting = opt["setting"]
        value = opt["value"]

        success, message = apply_setting(setting, value, system)
        if success:
            applied.append({"setting": setting, "value": value, "message": message})
        else:
            failed.append({"setting": setting, "value": value, "error": message})

    recommendations["mode"] = "applied"
    recommendations["applied"] = applied
    recommendations["failed"] = failed

    return recommendations


def apply_setting(setting: str, value, system: str) -> Tuple[bool, str]:
    """
    Apply a single setting
    Returns (success, message)
    """
    try:
        if setting == "set_power_mode":
            return apply_power_mode(value, system)
        elif setting == "set_process_priority":
            return True, f"Process priority recommendation: {value} (apply to specific process)"
        elif setting == "set_gpu_mode":
            return apply_gpu_mode(value, system)
        elif setting == "disable_background_apps":
            return True, "Recommendation: Close unnecessary background applications"
        elif setting == "set_cuda_visible_devices":
            os.environ["CUDA_VISIBLE_DEVICES"] = str(value)
            return True, f"Set CUDA_VISIBLE_DEVICES={value}"
        elif setting == "set_make_jobs":
            return True, f"Use 'make -j{value}' for parallel compilation"
        elif setting == "allocate_ram_percent":
            return True, f"Recommendation: Configure application to use up to {value}% RAM"
        elif setting == "enable_gpu_acceleration":
            return True, "Recommendation: Enable GPU acceleration in application settings"
        elif setting == "reduce_brightness":
            return apply_brightness(value, system)
        elif setting == "limit_cpu_percent":
            return True, f"Recommendation: Limit CPU-intensive processes to {value}%"
        elif setting == "set_fan_mode":
            return True, f"Recommendation: Set fan mode to '{value}' in BIOS/manufacturer software"
        else:
            return True, f"Recommendation: {setting} = {value}"
    except Exception as e:
        return False, str(e)


def apply_power_mode(mode: str, system: str) -> Tuple[bool, str]:
    """Apply power mode setting"""
    if system == "windows":
        # Windows power schemes
        schemes = {
            "high_performance": "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c",
            "balanced": "381b4222-f694-41f0-9685-ff5bb260df2e",
            "power_saver": "a1841308-3541-4fab-bc81-f71556f20b4a",
        }
        if mode in schemes:
            try:
                subprocess.run(
                    ["powercfg", "/setactive", schemes[mode]],
                    capture_output=True, check=True, timeout=10
                )
                return True, f"Set power plan to {mode}"
            except subprocess.SubprocessError as e:
                return False, f"Failed to set power plan: {e}"

    elif system == "linux":
        # Try to use cpupower or similar
        governors = {
            "high_performance": "performance",
            "balanced": "schedutil",
            "power_saver": "powersave",
        }
        if mode in governors:
            try:
                # This usually requires root
                result = subprocess.run(
                    ["cpupower", "frequency-set", "-g", governors[mode]],
                    capture_output=True, timeout=10
                )
                if result.returncode == 0:
                    return True, f"Set CPU governor to {governors[mode]}"
                else:
                    return True, f"Recommendation: Run 'sudo cpupower frequency-set -g {governors[mode]}'"
            except FileNotFoundError:
                return True, f"Recommendation: Install cpupower and run 'sudo cpupower frequency-set -g {governors[mode]}'"

    return True, f"Recommendation: Set power mode to {mode} in system settings"


def apply_gpu_mode(mode: str, system: str) -> Tuple[bool, str]:
    """Apply GPU mode setting"""
    if mode == "performance":
        return True, "Recommendation: Set GPU to 'Prefer Maximum Performance' in NVIDIA/AMD control panel"
    elif mode == "power_saving":
        return True, "Recommendation: Set GPU to 'Optimal Power' in NVIDIA/AMD control panel"
    return True, f"Recommendation: Set GPU mode to {mode}"


def apply_brightness(value: int, system: str) -> Tuple[bool, str]:
    """Apply brightness setting"""
    if system == "linux":
        try:
            # Try using brightnessctl
            subprocess.run(
                ["brightnessctl", "set", f"{value}%"],
                capture_output=True, timeout=10
            )
            return True, f"Set brightness to {value}%"
        except FileNotFoundError:
            pass

    return True, f"Recommendation: Set screen brightness to {value}%"


def print_recommendations(recommendations: Dict) -> None:
    """Pretty print recommendations"""
    print("\n" + "=" * 60)
    print(f"OPTIMIZATION REPORT: {recommendations['task'].upper()}")
    print("=" * 60)

    print(f"\nTask: {recommendations['task_description']}")

    print("\n--- System Summary ---")
    for key, value in recommendations["system_summary"].items():
        print(f"  {key.upper()}: {value}")

    if recommendations["suitability"]:
        print(f"\nSuitability: {recommendations['suitability'].upper()}")
    else:
        print("\nSuitability: Unknown (no matching rules)")

    if recommendations["warnings"]:
        print("\n--- Warnings ---")
        for warn in recommendations["warnings"]:
            print(f"  ⚠ {warn['message']}")

    if recommendations["optimizations"]:
        print("\n--- Recommended Optimizations ---")
        for i, opt in enumerate(recommendations["optimizations"], 1):
            print(f"  {i}. {opt['description']}")
    else:
        print("\n--- No specific optimizations available ---")

    if recommendations.get("mode") == "applied":
        if recommendations.get("applied"):
            print("\n--- Applied Settings ---")
            for item in recommendations["applied"]:
                print(f"  ✓ {item['message']}")
        if recommendations.get("failed"):
            print("\n--- Failed Settings ---")
            for item in recommendations["failed"]:
                print(f"  ✗ {item['setting']}: {item['error']}")

    print("\n" + "=" * 60)


if __name__ == "__main__":
    # Demo
    print("Available tasks:")
    for name, desc in list_tasks().items():
        print(f"  {name}: {desc}")

    print("\n\nGetting recommendations for 'gaming'...")
    recs = get_recommendations("gaming")
    print_recommendations(recs)
