from typing import Dict, Any, List
from rich.console import Console
from rich.table import Table
from rich.panel import Panel

from .hf_fetcher import get_model_parameters, get_model_config, ModelFetchError
from .calculator import calculate_memory_requirements, evaluate_hardware_compatibility, calculate_kv_cache_memory

# A curated list of highly recommended, modern Open Source LLMs
POPULAR_MODELS = [
    {"id": "meta-llama/Llama-3.2-1B-Instruct", "desc": "Great for edge/mobile devices"},
    {"id": "meta-llama/Llama-3.2-3B-Instruct", "desc": "Excellent small reasoning model"},
    {"id": "meta-llama/Meta-Llama-3.1-8B-Instruct", "desc": "Industry standard 8B model"},
    {"id": "meta-llama/Meta-Llama-3.1-70B-Instruct", "desc": "Heavyweight reasoning model"},
    {"id": "google/gemma-2-2b-it", "desc": "Google's lightweight instruction model"},
    {"id": "google/gemma-2-9b-it", "desc": "Google's powerful mid-size model"},
    {"id": "google/gemma-2-27b-it", "desc": "Google's flagship open weight model"},
    {"id": "Qwen/Qwen2.5-1.5B-Instruct", "desc": "Extremely fast, capable small model"},
    {"id": "Qwen/Qwen2.5-7B-Instruct", "desc": "Highly capable mathematics/coding model"},
    {"id": "Qwen/Qwen2.5-32B-Instruct", "desc": "Excellent balance of size and intelligence"},
    {"id": "Qwen/Qwen2.5-72B-Instruct", "desc": "Massive state-of-the-art open model"},
    {"id": "mistralai/Mistral-Nemo-Instruct-2407", "desc": "Mistral & NVIDIA 12B powerhouse"},
    {"id": "mistralai/Mixtral-8x7B-Instruct-v0.1", "desc": "Popular Sparse Mixture of Experts"},
    {"id": "microsoft/Phi-3.5-mini-instruct", "desc": "Microsoft's 3.8B highly-trained model"}
]

def generate_recommendations(hardware_profile: Dict[str, Any], console: Console):
    """
    Iterates over a curated list of models and finds the best quantization 
    that the user's hardware can comfortably support natively.
    """
    console.print(Panel("[bold green]Scanning and Recommending Models[/bold green]\nAnalyzing popular weights against your detected hardware limits..."))
    
    # We will fetch metadata for these models. Since fetching takes time per model, 
    # we use a rich progress status.
    
    recommendations_16bit = []
    recommendations_8bit = []
    recommendations_4bit = []
    
    with console.status("[bold cyan]Querying Hugging Face API for model metadata...", spinner="bouncingBar"):
        for model in POPULAR_MODELS:
            repo_id = model["id"]
            desc = model["desc"]
            
            try:
                # We do not use config.json for KV cache here to keep discovery fast, 
                # we fallback to the default 20% overhead calculator.
                params_b = get_model_parameters(repo_id)
                mem_reqs = calculate_memory_requirements(params_b)
                compats = evaluate_hardware_compatibility(mem_reqs, hardware_profile)
                
                # Check compatibility starting from highest quality
                if "🟢" in compats.get("FP16 / BF16 (No Quantization)", ""):
                    recommendations_16bit.append((repo_id, params_b, desc))
                elif "🟢" in compats.get("8-bit (INT8)", ""):
                    recommendations_8bit.append((repo_id, params_b, desc))
                elif "🟢" in compats.get("4-bit (INT4)", ""):
                    recommendations_4bit.append((repo_id, params_b, desc))
            except ModelFetchError:
                # Silently skip models we fail to fetch (e.g., if gated and no token is present)
                continue

    # Display results
    if not (recommendations_16bit or recommendations_8bit or recommendations_4bit):
        console.print("[bold red]Unfortunately, your system does not have enough memory to run any of the recommended models comfortably.[/bold red]")
        return
        
    rec_table = Table(title="Recommended Models for Your Hardware", show_header=True, header_style="bold magenta")
    rec_table.add_column("Hugging Face Model ID", style="cyan")
    rec_table.add_column("Size", justify="right", style="green")
    rec_table.add_column("Best Comfortable Quantization", style="yellow")
    rec_table.add_column("Notes", style="dim")

    # Sort each list by size descending just for clean presentation
    recommendations_16bit.sort(key=lambda x: x[1], reverse=True)
    recommendations_8bit.sort(key=lambda x: x[1], reverse=True)
    recommendations_4bit.sort(key=lambda x: x[1], reverse=True)

    for r in recommendations_16bit:
        rec_table.add_row(r[0], f"~{r[1]:.1f}B", "[green]Native 16-bit", r[2])
    for r in recommendations_8bit:
        rec_table.add_row(r[0], f"~{r[1]:.1f}B", "[yellow]8-bit (INT8)", r[2])
    for r in recommendations_4bit:
        rec_table.add_row(r[0], f"~{r[1]:.1f}B", "[red]4-bit (INT4)", r[2])
        
    console.print(rec_table)
    console.print("\n[dim]To inspect a specific model's exact memory footprint, run: llm-check <model-id>[/dim]")
