from __future__ import annotations

from dataclasses import dataclass, asdict
from typing import Any


@dataclass(frozen=True)
class ModelProfile:
    key: str
    display_name: str
    model_id: str
    purpose: str
    context_tokens: int
    deployment: str
    reasoning_mode: str
    reasoning_toggle: bool
    preserve_reasoning: bool
    temperature_default: float
    temperature_min: float
    temperature_max: float
    top_p_default: float
    max_output_default: int
    max_output_limit: int
    supports_tools: bool
    supports_structured: bool
    supports_vision: bool
    configured: bool
    notes: str

    def public(self) -> dict[str, Any]:
        value = asdict(self)
        value["settings_schema"] = settings_schema(self)
        return value


def build_profiles(config: dict[str, object]) -> dict[str, ModelProfile]:
    return {
        "k2.6": ModelProfile(
            key="k2.6",
            display_name="Kimi K2.6",
            model_id=str(config["K26_MODEL_ID"]),
            purpose="General chat, reasoning, vision and agent-ready work",
            context_tokens=262_144,
            deployment="Together serverless",
            reasoning_mode="hybrid",
            reasoning_toggle=True,
            preserve_reasoning=False,
            temperature_default=1.0,
            temperature_min=0.0,
            temperature_max=2.0,
            top_p_default=0.95,
            max_output_default=16_384,
            max_output_limit=32_768,
            supports_tools=True,
            supports_structured=True,
            supports_vision=True,
            configured=bool(config.get("TOGETHER_API_KEY") or config.get("FAKE_PROVIDER")),
            notes="Reasoning can be disabled for faster instant responses. Thinking uses temperature 1.0; instant mode defaults to 0.6.",
        ),
        "k2.7-code": ModelProfile(
            key="k2.7-code",
            display_name="Kimi K2.7 Code",
            model_id=str(config["K27_MODEL_ID"]),
            purpose="Coding-specialised, long-horizon software engineering",
            context_tokens=262_144,
            deployment="Together dedicated/custom endpoint",
            reasoning_mode="forced",
            reasoning_toggle=False,
            preserve_reasoning=True,
            temperature_default=1.0,
            temperature_min=1.0,
            temperature_max=1.0,
            top_p_default=0.95,
            max_output_default=24_576,
            max_output_limit=32_768,
            supports_tools=True,
            supports_structured=True,
            supports_vision=True,
            configured=bool(config.get("FAKE_PROVIDER") or (config.get("TOGETHER_API_KEY") and config.get("K27_CONFIGURED"))),
            notes="Thinking and preserved reasoning are locked on. Configure the exact Together dedicated endpoint/model identifier in .env.",
        ),
    }


def settings_schema(profile: ModelProfile) -> dict[str, Any]:
    return {
        "reasoning_enabled": {
            "type": "boolean",
            "editable": profile.reasoning_toggle,
            "forced": profile.reasoning_mode == "forced",
            "default": profile.reasoning_mode != "off",
        },
        "preserve_reasoning": {
            "type": "boolean",
            "editable": False,
            "forced": profile.preserve_reasoning,
            "default": profile.preserve_reasoning,
        },
        "temperature": {
            "type": "number",
            "editable": profile.temperature_min != profile.temperature_max,
            "min": profile.temperature_min,
            "max": profile.temperature_max,
            "step": 0.1,
            "default": profile.temperature_default,
        },
        "top_p": {"type": "number", "editable": True, "min": 0.1, "max": 1.0, "step": 0.05, "default": profile.top_p_default},
        "max_output_tokens": {"type": "integer", "editable": True, "min": 512, "max": profile.max_output_limit, "step": 512, "default": profile.max_output_default},
        "show_reasoning": {"type": "boolean", "editable": True, "default": True},
    }


def normalise_preferences(profile: ModelProfile, supplied: dict[str, Any] | None) -> dict[str, Any]:
    supplied = supplied or {}
    schema = settings_schema(profile)
    reasoning = bool(supplied.get("reasoning_enabled", schema["reasoning_enabled"]["default"]))
    if profile.reasoning_mode == "forced": reasoning = True
    temperature = float(supplied.get("temperature", profile.temperature_default))
    if not reasoning and profile.key == "k2.6" and "temperature" not in supplied:
        temperature = 0.6
    temperature = min(profile.temperature_max, max(profile.temperature_min, temperature))
    top_p = min(1.0, max(0.1, float(supplied.get("top_p", profile.top_p_default))))
    max_output = int(supplied.get("max_output_tokens", profile.max_output_default))
    max_output = min(profile.max_output_limit, max(512, max_output))
    return {
        "reasoning_enabled": reasoning,
        "preserve_reasoning": profile.preserve_reasoning,
        "temperature": temperature,
        "top_p": top_p,
        "max_output_tokens": max_output,
        "show_reasoning": bool(supplied.get("show_reasoning", True)),
    }
