from typing import List, Optional, Literal, Union

from pydantic import BaseModel, Field, ConfigDict
from pydantic.alias_generators import to_camel
from typing_extensions import TypedDict

from persona_models.common import create_uuid

SynthesizerType = Literal["gcloud", "elevenlabs", "gtts", "openai"]

LanguageCode = Literal["en-US", "it-IT", "es-ES", "fr-FR", "de-DE", "pt-BR"]


class SynthesizerConfiguration(BaseModel):
    enabled: Optional[bool] = True
    synthesizer_name: SynthesizerType = "gcloud"
    language_code: Optional[LanguageCode] = None
    voice: Optional[str] = None
    sample_rate_hertz: Optional[int] = 24000
    speed: Optional[float] = None
    similarity_boost: Optional[float] = None
    stability: Optional[float] = None
    style: Optional[float] = None
    voice_instructions: Optional[str] = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


TranscriberType = Literal["gcloud", "deepgram"]


class TranscriberConfiguration(BaseModel):
    enabled: Optional[bool] = True
    transcriber_name: TranscriberType = "gcloud"
    language_code: Optional[LanguageCode] = None
    sample_rate_hertz: Optional[int] = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


ModelName = Literal[
    # Google
    "gemini-3-pro",
    "gemini-2.5-pro",
    "gemini-2.5-flash",
    "gemini-2.5-flash-lite",
    "gemini-2.0-flash",
    "gemini-1.5-pro",
    "gemini-1.5-flash",
    "gemini-2.0-flash-001",
    "gemini-2.0-flash-lite",
    "gemini-2.0-flash-lite-001",
    "gemini-1.5-flash-latest",
    "gemini-1.5-flash-001",
    "gemini-1.5-flash-002",
    "gemini-1.5-pro-latest",
    "gemini-1.5-pro-001",
    "gemini-1.5-pro-002",
    # OpenAI
    "gpt-5.2",
    "gpt-5",
    "gpt-5-mini",
    "gpt-5-nano",
    "gpt-4.1",
    "gpt-4.1-mini",
    "gpt-4.1-nano",
    "gpt-4o",
    "gpt-4o-mini",
    "gpt-4.5",
    "o1",
    "o1-mini",
    "o3-mini",
    "o4-mini",
    "o4-mini-high",
    "gpt-3.5-turbo",
    "gpt-3.5-turbo-16k",
    # Fireworks
    "accounts/fireworks/models/minimax-m2p5",
    "accounts/fireworks/models/deepseek-v3p2",
    "accounts/fireworks/models/kimi-k2p5",
    "accounts/fireworks/models/glm-5",
    # Grok
    "grok-4-1-fast-reasoning",
    "grok-4-1-fast-non-reasoning",
    "grok-code-fast-1",
    "grok-4-fast-reasoning",
    "grok-4-fast-non-reasoning",
    "grok-4-0709",
    "grok-3-mini",
    "grok-3",
    "grok-2-vision-1212",
    # Ollama
    "ollama",
]


class ModelConfiguration(BaseModel):
    model_name: ModelName
    temperature: float = 1
    reasoning_effort: Optional[Literal["minimal", "low", "medium", "high"]] = None
    url: Optional[str] = None
    ollama_model_name: Optional[str] = None

    model_config = ConfigDict(
        alias_generator=to_camel, populate_by_name=True, use_enum_values=True
    )


ToolType = Literal["remote", "local"]


class ToolRemoteConfig(BaseModel):
    service_url: str
    secret: Optional[str] = None
    timeout: Optional[int] = 60

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class ToolProperty(TypedDict):
    type: str
    description: str


class ToolParameters(TypedDict):
    type: str
    title: str
    required: Optional[List[str]]
    properties: Optional[dict[str, ToolProperty]]


class ToolOutput(TypedDict):
    type: str
    title: str
    properties: Optional[dict[str, ToolProperty]]


class ToolLocalConfig(BaseModel):
    parameters: ToolParameters
    output: ToolOutput

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class Tool(BaseModel):
    type: ToolType = "remote"
    name: str
    description: Optional[str] = None
    config: Union[ToolRemoteConfig, ToolLocalConfig]

    def is_remote(self) -> bool:
        return self.type == "remote"

    def is_local(self) -> bool:
        return self.type == "local"

    model_config = ConfigDict(
        alias_generator=to_camel, populate_by_name=True, use_enum_values=True
    )


class ToolkitConfiguration(BaseModel):
    tools: List[Tool] | None = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


KnowledgeType = Literal["multi_hop", "simple"]


class MultiHopKnowledgeConfiguration(BaseModel):
    model: ModelConfiguration
    max_steps: int


class KnowledgeConfiguration(BaseModel):
    enabled: Optional[bool] = True
    knowledge_type: KnowledgeType = "multi_hop"
    namespace: str | list[str] | None = None
    number_of_entries: int = 3
    knowledge_base_id: str | None = None
    args: Union[MultiHopKnowledgeConfiguration, dict] = Field(
        default_factory=dict, alias="args"
    )

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class TwilioConfiguration(BaseModel):
    phone_number: str

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class WakeupConfiguration(BaseModel):
    enabled: Optional[bool] = False
    wakeup_words: List[str] = None
    sleep_words: Optional[List[str]] = None
    sleep_in_each_request: bool = False
    sleep_delay_seconds: Optional[int] = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class TelegramConfiguration(BaseModel):
    enabled: Optional[bool] = False
    bot_token: str
    enabled_users: List[str] | None = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class WhatsAppConfiguration(BaseModel):
    phone_number_id: str
    enabled_users: List[str] | None = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class Variable(BaseModel):
    name: str
    value: str | None = None
    description: str | None = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class Collaborator(BaseModel):
    agent_id: str
    scope: str

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


CollaborationModeType = Literal[
    "persistent_transfer", "delegate_and_return", "consult_then_respond"
]


class CollaborationConfiguration(BaseModel):
    enabled: Optional[bool] = False
    collaborators: List[Collaborator]
    mode: CollaborationModeType = "delegate_and_return"
    max_number_of_iterations: int = 10

    model_config = ConfigDict(
        alias_generator=to_camel, populate_by_name=True, use_enum_values=True
    )


AgentType = Literal["agent"]

AgentProtocol = Literal["webrtc", "twilio", "rest", "websocket", "telegram"]

StmType = Literal["simple", "summary"]


class NetworkConfiguration(BaseModel):
    allowed_websites: Optional[List[str]] = None
    allowed_ips: Optional[List[str]] = None


class StmConfiguration(BaseModel):
    type: StmType
    max_messages: int = 10
    model: ModelConfiguration | None = None
    max_number_of_words: int = 50
    include_tool_calls: bool = False

    model_config = ConfigDict(populate_by_name=True, alias_generator=to_camel)


class OrchestrationAgent(BaseModel):
    name: Optional[str] = None
    agent: str
    scope: Optional[str] = None

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class OrchestrationConfiguration(BaseModel):
    enabled: Optional[bool] = False
    agents: List[OrchestrationAgent]
    max_parallel_calls: int = 5

    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class AgentConfiguration(BaseModel):
    id: str = Field(default_factory=create_uuid)
    project_id: str | None = None
    api_key: str | None = None
    code: str | None = None
    name: str
    description: Optional[str] = None
    type: AgentType = "agent"
    synthesizer: Optional[SynthesizerConfiguration] = None
    transcriber: Optional[TranscriberConfiguration] = None
    model: ModelConfiguration
    system_instructions: Optional[str] = None
    toolkit: Optional[ToolkitConfiguration] = None
    variables: Optional[List[Variable]] = None
    enabled_protocols: List[AgentProtocol] | None = None
    twilio: Optional[TwilioConfiguration] = None
    language_code: Optional[LanguageCode] = None
    initial_message: Optional[str] = None
    knowledge: Optional[KnowledgeConfiguration] = None
    wakeup: Optional[WakeupConfiguration] = None
    telegram: Optional[TelegramConfiguration] = None
    whatsapp: Optional[WhatsAppConfiguration] = None
    collaboration: Optional[CollaborationConfiguration] = None
    orchestration: Optional[OrchestrationConfiguration] = None
    network: Optional[NetworkConfiguration] = None
    features: Optional[list] = None
    response_schema: Optional[dict] = None
    short_term_memory: Optional[StmConfiguration] = None
    verbose_errors: bool = False

    model_config = ConfigDict(
        alias_generator=to_camel, populate_by_name=True, use_enum_values=True
    )
