from __future__ import annotations

from datetime import datetime
from typing import Literal

from pydantic import BaseModel, Field, field_validator


class SettingsCompanyForm(BaseModel):
    companyName: str = Field(min_length=1, max_length=120)
    displayName: str = Field(min_length=1, max_length=120)
    companyCode: str = Field(default="", max_length=64)
    recordNumber: str = Field(default="", max_length=120)
    copyright: str = Field(default="", max_length=255)

    @field_validator("companyName", "displayName", "companyCode", "recordNumber", "copyright", mode="before")
    @classmethod
    def strip_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsAppearanceForm(BaseModel):
    themeSkin: str = Field(default="sky", max_length=64)

    @field_validator("themeSkin", mode="before")
    @classmethod
    def strip_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsAdminForm(BaseModel):
    adminAccount: str = Field(min_length=1, max_length=120)
    adminEmail: str = Field(default="", max_length=255)
    newPassword: str = Field(default="", max_length=128)
    confirmPassword: str = Field(default="", max_length=128)
    sessionTimeout: int = Field(default=30, ge=5, le=240)
    noticeEmail: str = Field(default="", max_length=255)
    mfaEnabled: bool = True
    strongPassword: bool = True
    loginAlertEnabled: bool = True
    adminPasswordConfigured: bool = False

    @field_validator("adminAccount", "adminEmail", "newPassword", "confirmPassword", "noticeEmail", mode="before")
    @classmethod
    def strip_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsSessionForm(BaseModel):
    conversationRetentionDays: int = Field(default=3, ge=1, le=10)


class SettingsModelRow(BaseModel):
    slot: str = Field(min_length=1, max_length=64)
    provider: str = Field(min_length=1, max_length=64)
    url: str = Field(min_length=1, max_length=512)
    apiKey: str = Field(default="", max_length=1024)
    apiKeyConfigured: bool = False
    modelId: str = Field(min_length=1, max_length=255)
    type: Literal["llm", "embedding", "rerank"] = "llm"

    @field_validator("slot", "provider", "url", "apiKey", "modelId", mode="before")
    @classmethod
    def strip_model_row_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsLlmForm(BaseModel):
    mainProvider: str = Field(min_length=1, max_length=64)
    mainModel: str = Field(min_length=1, max_length=255)
    mainEndpoint: str = Field(min_length=1, max_length=512)
    mainApiKey: str = Field(default="", max_length=1024)
    mainApiKeyConfigured: bool = False

    backupProvider: str = Field(min_length=1, max_length=64)
    backupModel: str = Field(min_length=1, max_length=255)
    backupEndpoint: str = Field(min_length=1, max_length=512)
    backupApiKey: str = Field(default="", max_length=1024)
    backupApiKeyConfigured: bool = False

    embeddingProvider: str = Field(min_length=1, max_length=64)
    embeddingModel: str = Field(min_length=1, max_length=255)
    embeddingEndpoint: str = Field(min_length=1, max_length=512)
    embeddingApiKey: str = Field(default="", max_length=1024)
    embeddingApiKeyConfigured: bool = False

    rerankerProvider: str = Field(min_length=1, max_length=64)
    rerankerModel: str = Field(min_length=1, max_length=255)
    rerankerEndpoint: str = Field(min_length=1, max_length=512)
    rerankerApiKey: str = Field(default="", max_length=1024)
    rerankerApiKeyConfigured: bool = False
    models: list[SettingsModelRow] = Field(default_factory=list)

    @field_validator(
        "mainProvider",
        "mainModel",
        "mainEndpoint",
        "mainApiKey",
        "backupProvider",
        "backupModel",
        "backupEndpoint",
        "backupApiKey",
        "embeddingProvider",
        "embeddingModel",
        "embeddingEndpoint",
        "embeddingApiKey",
        "rerankerProvider",
        "rerankerModel",
        "rerankerEndpoint",
        "rerankerApiKey",
        mode="before",
    )
    @classmethod
    def strip_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsLogForm(BaseModel):
    level: str = Field(min_length=1, max_length=16)
    retentionDays: int = Field(default=180, ge=1, le=3650)
    archiveCycle: str = Field(default="weekly", max_length=32)
    logPath: str = Field(min_length=1, max_length=255)
    alertEmail: str = Field(default="", max_length=255)
    operationAudit: bool = True
    loginAudit: bool = True
    maskSensitive: bool = True

    @field_validator("level", "archiveCycle", "logPath", "alertEmail", mode="before")
    @classmethod
    def strip_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsRenderForm(BaseModel):
    enabled: bool = False
    publicUrl: str = Field(default="", max_length=512)
    jwtSecret: str = Field(default="", max_length=1024)
    jwtSecretConfigured: bool = False

    @field_validator("publicUrl", "jwtSecret", mode="before")
    @classmethod
    def strip_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsMailForm(BaseModel):
    smtpHost: str = Field(min_length=1, max_length=255)
    port: int = Field(default=465, ge=1, le=65535)
    encryption: str = Field(default="SSL/TLS", max_length=32)
    senderName: str = Field(default="", max_length=120)
    senderAddress: str = Field(default="", max_length=255)
    username: str = Field(default="", max_length=255)
    password: str = Field(default="", max_length=1024)
    passwordConfigured: bool = False
    alertEnabled: bool = True
    digestEnabled: bool = False
    digestTime: str = Field(default="09:00", max_length=16)
    defaultReceiver: str = Field(default="", max_length=255)

    @field_validator(
        "smtpHost",
        "encryption",
        "senderName",
        "senderAddress",
        "username",
        "password",
        "digestTime",
        "defaultReceiver",
        mode="before",
    )
    @classmethod
    def strip_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class SettingsRead(BaseModel):
    companyForm: SettingsCompanyForm
    appearanceForm: SettingsAppearanceForm = Field(default_factory=SettingsAppearanceForm)
    adminForm: SettingsAdminForm
    sessionForm: SettingsSessionForm
    hermesForm: dict
    llmForm: SettingsLlmForm
    renderForm: SettingsRenderForm
    logForm: SettingsLogForm
    mailForm: SettingsMailForm


class SettingsWrite(BaseModel):
    companyForm: SettingsCompanyForm
    appearanceForm: SettingsAppearanceForm = Field(default_factory=SettingsAppearanceForm)
    adminForm: SettingsAdminForm
    sessionForm: SettingsSessionForm
    hermesForm: dict
    llmForm: SettingsLlmForm
    renderForm: SettingsRenderForm
    logForm: SettingsLogForm
    mailForm: SettingsMailForm


class ModelConnectivityTestRequest(BaseModel):
    provider: str = Field(min_length=1, max_length=64)
    endpoint: str = Field(min_length=1, max_length=512)
    model: str = Field(min_length=1, max_length=255)
    api_key: str | None = Field(default=None, max_length=1024)
    capability: Literal["chat", "embedding", "reranker"] = "chat"
    slot: str | None = Field(default=None, max_length=64)

    @field_validator("provider", "endpoint", "model", "api_key", mode="before")
    @classmethod
    def strip_model_string(cls, value: str | None) -> str | None:
        if value is None:
            return None
        return value.strip()


class ModelConnectivityTestRead(BaseModel):
    ok: bool
    provider: str
    model: str
    endpoint: str
    capability: str
    detail: str
    status_code: int | None = None
    checked_at: datetime


class SettingsCacheClearItemRead(BaseModel):
    cacheKey: str
    label: str
    clearedCount: int = Field(default=0, ge=0)


class SettingsCacheClearRead(BaseModel):
    totalCleared: int = Field(default=0, ge=0)
    items: list[SettingsCacheClearItemRead] = Field(default_factory=list)


class RuntimeModelConfigRead(BaseModel):
    slot: str
    provider: str
    model: str
    endpoint: str
    apiKey: str
    capability: Literal["chat", "embedding", "reranker"]
