//! Embedded builtin model catalogs (auto-generated by `generate-models chat` — do not edit).

use std::collections::HashMap;

use once_cell::sync::Lazy;
use serde::Deserialize;

use crate::types::{
    AnthropicMessagesCompat, Model, ModelCost, OpenAICompletionsCompat, OpenAIResponsesCompat, ThinkingLevelMap,
};

#[derive(Debug, Deserialize)]
struct RawModel {
    id: String,
    name: String,
    api: String,
    provider: String,
    #[serde(rename = "baseUrl")]
    base_url: String,
    reasoning: bool,
    #[serde(default)]
    #[serde(rename = "thinkingLevelMap")]
    thinking_level_map: Option<ThinkingLevelMap>,
    input: Vec<String>,
    cost: RawCost,
    #[serde(rename = "contextWindow")]
    context_window: u32,
    #[serde(rename = "maxTokens")]
    max_tokens: u32,
    #[serde(default)]
    headers: Option<HashMap<String, String>>,
    #[serde(default)]
    compat: Option<serde_json::Value>,
}

#[derive(Debug, Deserialize)]
struct RawCost {
    input: f64,
    output: f64,
    #[serde(rename = "cacheRead")]
    cache_read: f64,
    #[serde(rename = "cacheWrite")]
    cache_write: f64,
}

fn parse_models(json: &str) -> Vec<Model> {
    let raw: HashMap<String, RawModel> = serde_json::from_str(json).expect("invalid embedded model catalog");
    raw.into_values().map(convert_model).collect()
}

fn convert_model(raw: RawModel) -> Model {
    let (openai_completions_compat, openai_responses_compat, anthropic_compat) =
        parse_compat(&raw.api, raw.compat.as_ref());

    Model {
        id: raw.id,
        name: raw.name,
        api: raw.api,
        provider: raw.provider,
        base_url: raw.base_url,
        reasoning: raw.reasoning,
        thinking_level_map: raw.thinking_level_map,
        input: raw.input,
        cost: ModelCost {
            input: raw.cost.input,
            output: raw.cost.output,
            cache_read: raw.cost.cache_read,
            cache_write: raw.cost.cache_write,
        },
        context_window: raw.context_window,
        max_tokens: raw.max_tokens,
        headers: raw.headers,
        openai_completions_compat,
        openai_responses_compat,
        anthropic_compat,
    }
}

fn parse_compat(
    api: &str,
    compat: Option<&serde_json::Value>,
) -> (
    Option<OpenAICompletionsCompat>,
    Option<OpenAIResponsesCompat>,
    Option<AnthropicMessagesCompat>,
) {
    let Some(compat) = compat else {
        return (None, None, None);
    };
    match api {
        "openai-completions" => (serde_json::from_value(compat.clone()).ok(), None, None),
        "openai-responses" | "azure-openai-responses" | "openai-codex-responses" => {
            (None, serde_json::from_value(compat.clone()).ok(), None)
        }
        "anthropic-messages" => (None, None, serde_json::from_value(compat.clone()).ok()),
        _ => (None, None, None),
    }
}

macro_rules! define_catalog {
    ($name:ident, $file:literal) => {
        pub static $name: Lazy<Vec<Model>> = Lazy::new(|| {
            parse_models(include_str!(concat!(
                env!("CARGO_MANIFEST_DIR"),
                "/models/",
                $file
            )))
        });
    };
}

define_catalog!(AMAZON_BEDROCK_MODELS, "amazon_bedrock.json");
define_catalog!(ANT_LING_MODELS, "ant_ling.json");
define_catalog!(ANTHROPIC_MODELS, "anthropic.json");
define_catalog!(AZURE_OPENAI_RESPONSES_MODELS, "azure_openai_responses.json");
define_catalog!(CEREBRAS_MODELS, "cerebras.json");
define_catalog!(CLOUDFLARE_AI_GATEWAY_MODELS, "cloudflare_ai_gateway.json");
define_catalog!(CLOUDFLARE_WORKERS_AI_MODELS, "cloudflare_workers_ai.json");
define_catalog!(DEEPSEEK_MODELS, "deepseek.json");
define_catalog!(FIREWORKS_MODELS, "fireworks.json");
define_catalog!(GITHUB_COPILOT_MODELS, "github_copilot.json");
define_catalog!(GOOGLE_MODELS, "google.json");
define_catalog!(GOOGLE_VERTEX_MODELS, "google_vertex.json");
define_catalog!(GROQ_MODELS, "groq.json");
define_catalog!(HUGGINGFACE_MODELS, "huggingface.json");
define_catalog!(KIMI_CODING_MODELS, "kimi_coding.json");
define_catalog!(MINIMAX_MODELS, "minimax.json");
define_catalog!(MINIMAX_CN_MODELS, "minimax_cn.json");
define_catalog!(MISTRAL_MODELS, "mistral.json");
define_catalog!(MOONSHOTAI_MODELS, "moonshotai.json");
define_catalog!(MOONSHOTAI_CN_MODELS, "moonshotai_cn.json");
define_catalog!(NVIDIA_MODELS, "nvidia.json");
define_catalog!(OPENAI_MODELS, "openai.json");
define_catalog!(OPENAI_CODEX_MODELS, "openai_codex.json");
define_catalog!(OPENCODE_MODELS, "opencode.json");
define_catalog!(OPENCODE_GO_MODELS, "opencode_go.json");
define_catalog!(OPENROUTER_MODELS, "openrouter.json");
define_catalog!(TOGETHER_MODELS, "together.json");
define_catalog!(VERCEL_AI_GATEWAY_MODELS, "vercel_ai_gateway.json");
define_catalog!(XAI_MODELS, "xai.json");
define_catalog!(XIAOMI_MODELS, "xiaomi.json");
define_catalog!(XIAOMI_TOKEN_PLAN_AMS_MODELS, "xiaomi_token_plan_ams.json");
define_catalog!(XIAOMI_TOKEN_PLAN_CN_MODELS, "xiaomi_token_plan_cn.json");
define_catalog!(XIAOMI_TOKEN_PLAN_SGP_MODELS, "xiaomi_token_plan_sgp.json");
define_catalog!(ZAI_MODELS, "zai.json");
define_catalog!(ZAI_CODING_CN_MODELS, "zai_coding_cn.json");

pub fn all_builtin_models() -> HashMap<&'static str, &'static [Model]> {
    HashMap::from([
        ("amazon-bedrock", AMAZON_BEDROCK_MODELS.as_slice()),
        ("ant-ling", ANT_LING_MODELS.as_slice()),
        ("anthropic", ANTHROPIC_MODELS.as_slice()),
        ("azure-openai-responses", AZURE_OPENAI_RESPONSES_MODELS.as_slice()),
        ("cerebras", CEREBRAS_MODELS.as_slice()),
        ("cloudflare-ai-gateway", CLOUDFLARE_AI_GATEWAY_MODELS.as_slice()),
        ("cloudflare-workers-ai", CLOUDFLARE_WORKERS_AI_MODELS.as_slice()),
        ("deepseek", DEEPSEEK_MODELS.as_slice()),
        ("fireworks", FIREWORKS_MODELS.as_slice()),
        ("github-copilot", GITHUB_COPILOT_MODELS.as_slice()),
        ("google", GOOGLE_MODELS.as_slice()),
        ("google-vertex", GOOGLE_VERTEX_MODELS.as_slice()),
        ("groq", GROQ_MODELS.as_slice()),
        ("huggingface", HUGGINGFACE_MODELS.as_slice()),
        ("kimi-coding", KIMI_CODING_MODELS.as_slice()),
        ("minimax", MINIMAX_MODELS.as_slice()),
        ("minimax-cn", MINIMAX_CN_MODELS.as_slice()),
        ("mistral", MISTRAL_MODELS.as_slice()),
        ("moonshotai", MOONSHOTAI_MODELS.as_slice()),
        ("moonshotai-cn", MOONSHOTAI_CN_MODELS.as_slice()),
        ("nvidia", NVIDIA_MODELS.as_slice()),
        ("openai", OPENAI_MODELS.as_slice()),
        ("openai-codex", OPENAI_CODEX_MODELS.as_slice()),
        ("opencode", OPENCODE_MODELS.as_slice()),
        ("opencode-go", OPENCODE_GO_MODELS.as_slice()),
        ("openrouter", OPENROUTER_MODELS.as_slice()),
        ("together", TOGETHER_MODELS.as_slice()),
        ("vercel-ai-gateway", VERCEL_AI_GATEWAY_MODELS.as_slice()),
        ("xai", XAI_MODELS.as_slice()),
        ("xiaomi", XIAOMI_MODELS.as_slice()),
        ("xiaomi-token-plan-ams", XIAOMI_TOKEN_PLAN_AMS_MODELS.as_slice()),
        ("xiaomi-token-plan-cn", XIAOMI_TOKEN_PLAN_CN_MODELS.as_slice()),
        ("xiaomi-token-plan-sgp", XIAOMI_TOKEN_PLAN_SGP_MODELS.as_slice()),
        ("zai", ZAI_MODELS.as_slice()),
        ("zai-coding-cn", ZAI_CODING_CN_MODELS.as_slice()),
    ])
}

pub fn get_builtin_model(provider: &str, id: &str) -> Option<Model> {
    all_builtin_models().get(provider)?.iter().find(|m| m.id == id).cloned()
}

pub fn get_builtin_models(provider: &str) -> Vec<Model> {
    all_builtin_models()
        .get(provider)
        .map(|models| models.to_vec())
        .unwrap_or_default()
}

pub fn get_builtin_providers() -> Vec<&'static str> {
    let mut providers: Vec<_> = all_builtin_models().keys().copied().collect();
    providers.sort_unstable();
    providers
}
