"""Local router configuration.

Hardcoded VRAM-tier model mapping, task-specific temperatures,
and response mode presets (smart/smarter/smartest).
Zero external dependencies -- pure Python data structures.
"""

from __future__ import annotations

from typing import Literal, TypedDict


class ModelMapping(TypedDict):
    """VRAM tier -> recommended model."""

    vram_range: tuple[int, int]  # MB range (min, max)
    model: str
    tier: int


# =====================================================================
# Default Model
# =====================================================================
# Single source of truth for the primary model name.
# All fallbacks and preference lists reference this constant.

DEFAULT_MODEL = "qwen3.5:9b"


# =====================================================================
# VRAM-Tier Model Mapping
# =====================================================================
# Maps available VRAM to recommended model.
# Sorted by increasing VRAM requirement.

VRAM_TIER_MODELS: list[ModelMapping] = [
    {
        "vram_range": (0, 4096),  # <4GB VRAM
        "model": "qwen3.5:2b",
        "tier": 1,
    },
    {
        "vram_range": (4096, 8192),  # 4-8GB VRAM
        "model": "qwen3.5:4b",
        "tier": 2,
    },
    {
        "vram_range": (8192, 10240),  # 8-10GB VRAM
        "model": "qwen3.5:9b",
        "tier": 3,
    },
    {
        "vram_range": (10240, 999999),  # 10GB+ VRAM
        "model": "qwen3.5:9b",
        "tier": 4,
    },
]


# =====================================================================
# Task-Specific Temperatures
# =====================================================================
# Conservative defaults prevent hallucination.
# Lower temp = more deterministic, higher = more creative.

TASK_TEMPERATURES: dict[str, float] = {
    "code": 0.15,  # Code generation: very deterministic
    "reasoning": 0.3,  # Reasoning tasks: moderate determinism
    "general": 0.4,  # General chat: balanced
    "creative": 0.7,  # Creative writing: more variation
}

DEFAULT_TEMPERATURE = 0.4


# =====================================================================
# Model Descriptions (for setup wizard display)
# =====================================================================

MODEL_DESCRIPTIONS: dict[str, dict[str, str]] = {
    "qwen3.5:2b": {
        "size": "~2.7 GB",
        "summary": "Lightweight multimodal model with 256K context, fast on any hardware",
    },
    "qwen3.5:4b": {
        "size": "~3.4 GB",
        "summary": "Compact multimodal model with 256K context, good balance of speed and quality",
    },
    "qwen3.5:9b": {
        "size": "~6.6 GB",
        "summary": "Strong reasoning and coding with 256K context, great for most tasks",
    },
}


# =====================================================================
# Response Mode Configuration
# =====================================================================
# Three tiers: Smart (fast), Smarter (thinking, default), Smartest (local+cloud).
# Smartest requires a cloud API key -- gated at runtime.

ResponseMode = Literal["smart", "smarter", "smartest"]

RESPONSE_MODE_PARAMS: dict[str, dict] = {
    "smart": {
        "think": False,
        "num_predict": 4096,    # 4K budget: response tokens only
        "keep_alive": "2m",
    },
    "smarter": {
        "think": True,
        "num_predict": 16384,   # 16K budget: ~8K thinking + ~8K response
        "keep_alive": "2m",
    },
    "smartest": {
        "think": True,
        "num_predict": 16384,   # Phase 1 params (same as smarter)
        "keep_alive": "2m",
    },
}

DEFAULT_RESPONSE_MODE: ResponseMode = "smarter"
DEFAULT_KEEP_ALIVE = "2m"

# =====================================================================
# Distillation Configuration (Phase 2 of Smartest pipeline)
# =====================================================================
# Local model compresses its reasoning into a structured briefing for the cloud model.

DISTILLATION_PARAMS: dict = {
    "think": False,
    "num_predict": 8192,
    "keep_alive": "2m",
    "temperature": 0.15,        # Very deterministic extraction
}

DISTILLATION_PROMPT = (
    "You are distilling an AI agent's detailed analysis into a briefing "
    "for a senior AI model. Your job is to preserve ALL substantive "
    "content while stripping noise. Remove meta-commentary, hedging, filler, "
    "and repetition -- but keep every concrete fact, data point, code snippet, "
    "specific recommendation, and technical detail.\n\n"
    "Scale your output to match the substance:\n"
    "- Simple question with a clear answer: 50-200 words\n"
    "- Moderate analysis with several points: 200-1000 words\n"
    "- Complex multi-faceted analysis: 1000-5000 words\n"
    "Never pad short answers. Never truncate rich analysis.\n\n"
    "Output these sections (skip any that are empty):\n\n"
    "### Key Findings\n"
    "- Core observations, conclusions, data points, and code examples\n\n"
    "### Recommended Approach\n"
    "- Specific actions, implementations, or solutions proposed\n\n"
    "### Constraints & Caveats\n"
    "- Limitations, risks, or things explicitly noted to avoid\n\n"
    "### Confidence Assessment\n"
    "- One line: High/Medium/Low and why\n\n"
    "--- ORIGINAL ANALYSIS ---\n"
    "{qwen_output}\n"
    "--- END ---\n\n"
    "Distilled briefing:"
)

# =====================================================================
# Smartest Cloud Prompt (Phase 3 of Smartest pipeline)
# =====================================================================
# Cloud model gets persona + distilled briefing, NOT full channel history.

SMARTEST_CLOUD_PROMPT = (
    "You are responding as the {agent_id} agent.\n\n"
    "Follow this persona exactly:\n"
    "---\n{persona}\n---\n\n"
    "{grounding_rules}\n\n"
    "A local AI model has analyzed the conversation and produced this briefing:\n\n"
    "--- ANALYSIS BRIEFING ---\n"
    "{distilled_briefing}\n"
    "--- END BRIEFING ---\n\n"
    "Now respond to the user's message. Use the briefing as your research/context, "
    "but write your response in your own voice. Do not reference the briefing or "
    "the local model's analysis.\n\n"
    "User message:\n{user_message}"
)

# =====================================================================
# Roundtable Model Preferences
# =====================================================================
# Auto-selection fallback order for compiled roundtable when no model
# is explicitly provided. First match against installed models wins.

ROUNDTABLE_MODEL_PREFERENCES: list[str] = [
    DEFAULT_MODEL,
    "qwen3.5:35b-a3b", "qwen3.5:4b",
    "qwen3.5:2b",
]

# Empty-response retry: if tokens_out exceeds this but visible content
# is shorter than MIN_CONTENT_CHARS, the model spent all tokens thinking.
THINKING_DRAIN_TOKEN_THRESHOLD = 200
MIN_CONTENT_CHARS = 20


def get_model_for_vram(vram_mb: int) -> str:
    """Get recommended model for available VRAM.

    Args:
        vram_mb: Available VRAM in megabytes

    Returns:
        Model name (e.g., "qwen3.5:9b")
    """
    for mapping in VRAM_TIER_MODELS:
        min_vram, max_vram = mapping["vram_range"]
        if min_vram <= vram_mb < max_vram:
            return mapping["model"]

    # Fallback: smallest model
    return VRAM_TIER_MODELS[0]["model"]


def get_tier_for_model(model_name: str) -> int | None:
    """Get the tier number for a model name.

    Args:
        model_name: Model name (e.g., "qwen3.5:9b")

    Returns:
        Tier number (1-4) or None if model not in mapping.
    """
    for mapping in VRAM_TIER_MODELS:
        if mapping["model"] == model_name:
            return mapping["tier"]
    return None


def get_temperature(task_type: str | None = None) -> float:
    """Get temperature for task type.

    Args:
        task_type: Task type (code, reasoning, general, creative)

    Returns:
        Temperature value (0.0-1.0)
    """
    if task_type is None:
        return DEFAULT_TEMPERATURE
    return TASK_TEMPERATURES.get(task_type.lower(), DEFAULT_TEMPERATURE)


# =====================================================================
# Conversation Learning Configuration
# =====================================================================
# Post-response fact extraction: local Qwen extracts durable knowledge
# from agent conversations. Runs async (daemon thread) after response
# is delivered -- never blocks chat.

LEARNING_ENABLED = True

LEARNING_EXTRACTION_PARAMS: dict = {
    "think": False,
    "num_predict": 2048,
    "keep_alive": "2m",
    "temperature": 0.15,        # Very deterministic extraction
}

LEARNING_MIN_RESPONSE_LENGTH = 80       # Skip very short responses entirely
LEARNING_GATE_THRESHOLD = 200           # Response length for gate consideration
LEARNING_DEDUP_THRESHOLD = 0.80         # Term overlap to consider duplicate
LEARNING_MAX_FACTS_PER_AGENT = 200      # Cap per agent (drop oldest low-conf)
LEARNING_PROFILE_EVOLVE_DAYS = 7        # Min days between profile re-distillation
LEARNING_PROFILE_MIN_NEW_PREFS = 10     # Min new preference facts before evolving

# Agents that should never trigger extraction (orchestrators, system)
LEARNING_SKIP_AGENTS: set[str] = {
    "system", "cohort_orchestrator", "orchestrator",
}

LEARNING_EXTRACTION_PROMPT = (
    "Extract durable facts from this conversation that would be useful in "
    "future conversations with this user. Only extract facts that represent "
    "lasting knowledge, not ephemeral details.\n\n"
    "Categories:\n"
    "- domain_fact: Technical knowledge, concepts, how things work\n"
    "- procedure: Step-by-step processes, workflows, commands\n"
    "- preference: User's stated preferences about communication or approach\n"
    "- correction: User redirecting the agent (implies a rule for the future)\n"
    "- tool_usage: Specific tool configurations, settings, or patterns\n\n"
    "Corrections are when the user redirects: 'no, I meant...', 'don't do X', "
    "'shorter please'. Store the RULE, not the specific instance.\n"
    "Example: User says 'shorter please' -> "
    'fact: "User prefers concise responses", category: "preference"\n\n'
    "Output a JSON array. Each item: "
    '{{"fact": "...", "confidence": "high|medium|low", '
    '"category": "domain_fact|procedure|preference|correction|tool_usage"}}\n\n'
    "If nothing is worth extracting, output: []\n\n"
    "User: {message}\n"
    "Agent ({agent_id}): {response}\n"
)

LEARNING_PROFILE_DISTILL_PROMPT = (
    "Based on these user preference and correction observations, build a "
    "communication profile for this user. Output valid JSON only.\n\n"
    '{{"core_paragraph": "One paragraph summarizing who this user is and how '
    'they prefer to communicate",'
    ' "adaptation_rules": {{'
    ' "response_length": "minimal|short|medium|detailed",'
    ' "summarize_back": true or false,'
    ' "confirm_decisions": true or false,'
    ' "praise_before_feedback": true or false,'
    ' "options_per_question": 1 to 5,'
    ' "custom_rules": ["list of specific rules from observations"]}}}}\n\n'
    "Observations:\n{observations}\n"
)
