from __future__ import annotations

from functools import cached_property
from typing import Any

from pydantic import Field

from ..base import APIDataModel


# Response models
class CollectResponse(APIDataModel):
    id: str


class ReleatedCharacter(APIDataModel):
    character_id: str
    character_name: str


class Bg(APIDataModel):
    nsfw: int
    url: str
    video: str
    width: int
    height: int


class Bg1(APIDataModel):
    nsfw: int
    url: str
    video: str
    width: int
    height: int


class OriginalVision(APIDataModel):
    bg: Bg


class Cover(APIDataModel):
    cover: str | None = None
    cover_width: int | None = None
    cover_height: int | None = None
    cover_nsfw: int | None = None
    url: str | None = None
    width: int | None = None
    height: int | None = None
    nsfw: int | None = None
    video: str | None = None


class Stat(APIDataModel):
    num_of_like: int
    num_of_collection: int
    num_of_task: int
    num_of_view: int
    num_of_token: int | None = None
    num_of_artwork: int
    rating: int


class OriginalCover(APIDataModel):
    nsfw: int
    url: str
    video: str
    width: int
    height: int


class LatestSessionItem(APIDataModel):
    session_id: str
    create_at: int
    name: str
    latest_at: int
    is_first_msg: int


class Category(APIDataModel):
    id: str
    name: str
    type: int


class Vision(APIDataModel):
    bg: Bg1


class Author(APIDataModel):
    id: str
    avatar_frame: str
    name: str
    num_of_character: int
    join_stimulate: int
    head: str
    is_follow: bool
    follower_cnt: int
    is_active: bool


class CharacterDetail(APIDataModel):
    personal: str
    is_deep_seek: int
    seo_keywords: str
    agent_simple_image: list[Any]
    releated_character: ReleatedCharacter
    apply_id: str
    default_apply_id: str
    gender: str
    mask: str
    dialog_example: str
    allow_same: int
    pop_recommend_model: int
    original_vision: OriginalVision
    audio_url: str
    evaluation: str
    gpt_model: str
    init_mem_num: int
    name: str
    seo_session_keywords: str
    is_show_both_sidebar: bool
    cover: Cover
    scene: str
    support_first_voice: int
    character_type: int
    id: str
    support_models: Any
    support_voice: int
    seo_description: str
    seo_session_title: str
    status: int
    nsfw_level: int
    timbre_id: str
    chat_people_num: int
    fuzzy_start_number: int
    create_at: int
    first_msg: str
    comment_num: int
    workflow_no: str
    act_model_setting: Any
    tourist_limit_num: int
    intro: str
    publish_status: int
    collect: Any
    source_from: int
    nsfw: int
    support_reply_img: int
    collect_num: int
    source: str
    session_id: str
    free_chat_nums: int
    chat_free_pattern_num: int
    default_workflow_no: str
    stat: Stat
    default_model: str
    original_cover: OriginalCover
    vip_only: int
    latest_session: list[LatestSessionItem]
    seo_title: str
    name_key: str
    seo_session_description: str
    categories: list[Category]
    vision: Vision
    config: str
    is_official_agent: int
    video_url: str
    support_generate_img: int
    author: Author


class ListItem(APIDataModel):
    gpt_model: str
    title_key: str
    lang_key: str
    is_svip: bool
    is_svip_free: bool
    is_new: bool
    is_price_reduction: bool
    is_free: bool
    score: float
    temperature_min: float
    temperature_max: float
    msg_len_min: int
    msg_len_max: int
    diversity_min: float
    diversity_max: int
    default_temperature: float
    default_diversity: float
    default_length_of_reply: int
    input_consume: float
    output_consume: float


class FunctionalityListItem(APIDataModel):
    gpt_model: str
    title_key: str
    lang_key: str
    is_svip: bool
    is_svip_free: bool
    is_new: bool
    is_price_reduction: bool
    is_free: bool
    score: float
    temperature_min: float
    temperature_max: float
    msg_len_min: int
    msg_len_max: int
    diversity_min: int
    diversity_max: int
    default_temperature: float
    default_diversity: int
    default_length_of_reply: int
    input_consume: float
    output_consume: float


class ActivityItem(APIDataModel):
    discount_activity: bool


class WorkflowListItem(APIDataModel):
    workflow_no: str
    icon: str
    title_key: str
    desc_key: str
    highly_recommend: str
    recommend: list[str]
    workflow_path: str


class ModelTitle(APIDataModel):
    Mistral: str | None = None
    Grok_3_Beta: str | None = Field(None, alias="Grok-3-Beta")
    Seaart_Free_2_0: str | None = Field(None, alias="Seaart-Free-2.0")
    doubao_pro32k: str | None = None
    gemini_3_flash_preview: str | None = Field(None, alias="gemini-3-flash-preview")
    glm_4_5_air: str | None = Field(None, alias="glm-4.5-air")
    xingchen_plus_ja: str | None = Field(None, alias="xingchen-plus-ja")
    doubao_skylark_pro: str | None = None
    Deepseek_v3: str | None = Field(None, alias="Deepseek-v3")
    Normal: str | None = None
    Claude_3_5_Sonnet: str | None = Field(None, alias="Claude-3.5-Sonnet")
    GPT_4_Turbo_2024_0409: str | None = Field(None, alias="GPT-4-Turbo-2024-0409")
    LongCat_Flash_Chat: str | None = Field(None, alias="LongCat-Flash-Chat")
    Gemini_2_5_pro_preview_06_05: str | None = Field(
        None, alias="Gemini-2.5-pro-preview-06-05"
    )
    qwq_plus: str | None = Field(None, alias="qwq-plus")
    Hermes3: str | None = None
    Wizardlm: str | None = None
    Seaart_Free: str | None = Field(None, alias="Seaart-Free")
    Deepseek_R1: str | None = Field(None, alias="Deepseek-R1")
    GPT_4_Turbo_1106: str | None = Field(None, alias="GPT-4-Turbo-1106")
    Grok_3_Mini_Beta: str | None = Field(None, alias="Grok-3-Mini-Beta")
    GPT_35_Turbo_1106: str | None = Field(None, alias="GPT-35-Turbo-1106")
    Gemini_2_5_Flash_Preview_05_20: str | None = Field(
        None, alias="Gemini-2.5-Flash-Preview-05-20"
    )
    hunyuan_turbos_role_20251114: str | None = Field(
        None, alias="hunyuan-turbos-role-20251114"
    )
    hunyuan_turbos_latest: str | None = Field(None, alias="hunyuan-turbos-latest")
    Claude_3_7_Sonnet: str | None = Field(None, alias="Claude-3.7-Sonnet")
    gemini_3_pro_preview: str | None = Field(None, alias="gemini-3-pro-preview")
    glm_4_plus: str | None = Field(None, alias="glm-4-plus")


class DefaultRecommend(APIDataModel):
    tourist_default_model: str
    tourist_default_workflow_no: str
    default_model: str
    default_workflow_no: str
    free_model_mapping: str


class TileListItem(APIDataModel):
    gpt_model: str
    title_key: str
    lang_key: str
    is_svip: bool
    is_svip_free: bool
    is_new: bool
    is_price_reduction: bool
    is_free: bool
    score: float
    temperature_min: float
    temperature_max: float
    msg_len_min: int
    msg_len_max: int
    diversity_min: float
    diversity_max: int
    default_temperature: float
    default_diversity: float
    default_length_of_reply: int
    input_consume: float
    output_consume: float


class CharacterListModels(APIDataModel):
    List: list[ListItem]
    functionality_list: list[FunctionalityListItem]
    vip_open_use_key: list[str]
    activity_item: ActivityItem
    workflow_list: list[WorkflowListItem]
    model_title: ModelTitle
    default_recommend: DefaultRecommend
    tile_list: list[TileListItem]
    svip_free_model: list[str]
    show_version: str

    @cached_property
    def _by_title_map(self) -> dict[str, ListItem]:
        return {m.title_key.casefold(): m for m in self.List}

    def by_title(self, model_title: str) -> ListItem | None:
        """Look up a model by its title key (case-insensitive).

        Args:
            model_title: Exact title to look up (matched case-insensitively).

        Returns:
            The matching :class:`ListItem`, or ``None`` if not found.

        Example:
            >>> models = (await client.characters.models()).value
            >>> item = models.by_title("Seaart-Free")
        """
        return self._by_title_map.get(model_title.casefold())

    def search(self, query: str) -> list[ListItem]:
        """Search models by a substring of their title key (case-insensitive).

        Args:
            query: Substring to search for in ``title_key``.

        Returns:
            All :class:`ListItem` entries whose ``title_key`` contains
            ``query``.

        Example:
            >>> models = (await client.characters.models()).value
            >>> gpt_models = models.search("gpt")
        """
        q = query.casefold()
        return [m for m in self.List if q in m.title_key.casefold()]
