"""应用配置数据模型。

对应数据库 app_configs 表。支持公共配置（public）和用户私有配置（user）两种类型。
此模块位于 hezor_common，可被 hezor2 及其他项目（如 SDK）共享使用。
"""

import re
from datetime import datetime
from enum import Enum

from pydantic import BaseModel, ConfigDict, Field, field_validator

from hezor_common.utilities.config_interpolation import (
    SUPPORTED_INTERPOLATION_VARS,
    ContextVariables,
)

_KEY_PATTERN = re.compile(r"^[A-Z][A-Z0-9_]{0,198}$")
_VAR_IN_VALUE_PATTERN = re.compile(r"\$\{([A-Z_][A-Z0-9_]*)\}")

# 平台保留前缀，仅 public config 可使用
RESERVED_KEY_PREFIXES: tuple[str, ...] = ("HEZOR_",)


def _validate_interpolation_vars(value: str) -> str:
    """校验 value 中的 ${VAR} 均为已支持的内置变量。

    Parameters
    ----------
    value : str
        配置值字符串。

    Returns
    -------
    str
        校验通过的 value。

    Raises
    ------
    ValueError
        若 value 中存在不受支持的 ${VAR} 占位符。
    """
    unknown = [
        var
        for var in _VAR_IN_VALUE_PATTERN.findall(value)
        if var not in SUPPORTED_INTERPOLATION_VARS
    ]
    if unknown:
        supported = ", ".join(sorted(SUPPORTED_INTERPOLATION_VARS))
        raise ValueError(
            f"不支持的插值变量：${{{'}, ${'.join(unknown)}}}。当前支持的变量：{supported}"
        )
    return value


class AppConfigType(str, Enum):
    """配置类型枚举。"""

    public = "public"
    user = "user"


class AppConfig(BaseModel):
    """应用配置数据库模型。

    对应 app_configs 表结构。

    Attributes
    ----------
    id : str
        配置 ID（UUID）。
    config_type : AppConfigType
        配置类型：public / user。
    user_id : str | None
        所属用户（public 类型为 None）。
    key : str
        配置键（大写 + 数字 + 下划线）。
    value : str
        配置值（可包含 ${VAR} 插值占位符）。
    description : str | None
        说明。
    is_secret : bool
        是否敏感值（列表查询时屏蔽明文，Webhook 下发时照常返回）。
    created_by : str
        创建者 user_id（审计）。
    created_at : datetime
        创建时间。
    updated_at : datetime
        更新时间。
    is_deleted : bool
        是否已删除（软删除）。

    Examples
    --------
    >>> config = AppConfig(
    ...     id="123e4567-e89b-12d3-a456-426614174000",
    ...     config_type=AppConfigType.user,
    ...     user_id="user_12345",
    ...     key="MY_APP_URL",
    ...     value="https://example.com/api",
    ...     is_secret=False,
    ...     created_by="user_12345",
    ...     created_at=datetime(2026, 3, 20),
    ...     updated_at=datetime(2026, 3, 20),
    ... )
    """

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "id": "123e4567-e89b-12d3-a456-426614174000",
                "config_type": "user",
                "user_id": "user_12345",
                "key": "MY_APP_URL",
                "value": "https://${GLOBAL_BASE_URL}/api",
                "description": "应用访问地址",
                "is_secret": False,
                "created_by": "user_12345",
                "created_at": "2026-03-20T10:00:00Z",
                "updated_at": "2026-03-20T10:00:00Z",
                "is_deleted": False,
            }
        }
    )

    id: str = Field(..., description="配置 ID（UUID）")
    config_type: AppConfigType = Field(..., description="配置类型：public / user")
    user_id: str | None = Field(default=None, description="所属用户（public 类型为 None）")
    key: str = Field(..., description="配置键（大写 + 数字 + 下划线）")
    value: str = Field(..., description="配置值（可包含 ${VAR} 插值占位符）")
    description: str | None = Field(default=None, description="说明")
    is_secret: bool = Field(default=False, description="是否敏感值（列表查询时屏蔽明文）")
    created_by: str = Field(..., description="创建者 user_id（审计）")
    created_at: datetime = Field(..., description="创建时间")
    updated_at: datetime = Field(..., description="更新时间")
    is_deleted: bool = Field(default=False, description="是否已删除（软删除）")


class AppConfigCreate(BaseModel):
    """创建配置请求。

    key 命名规范：大写字母开头，仅允许大写字母、数字、下划线，长度 1-200。
    用户配置的 key 不可使用平台保留前缀（HEZOR_）。

    Parameters
    ----------
    key : str
        配置键（大写字母开头，仅大写字母 / 数字 / 下划线）。
    value : str
        配置值（支持 ${GLOBAL_BASE_URL} 等内置变量）。
    description : str | None
        说明（可选）。
    is_secret : bool
        是否敏感值，默认 False。

    Examples
    --------
    >>> req = AppConfigCreate(key="MY_API_KEY", value="secret_value", is_secret=True)
    """

    key: str = Field(..., description="配置键（大写字母开头，仅大写字母 / 数字 / 下划线）")
    value: str = Field(..., description="配置值（支持 ${GLOBAL_BASE_URL} 等内置变量）")
    description: str | None = Field(default=None, description="说明（可选）")
    is_secret: bool = Field(default=False, description="是否敏感值")

    @field_validator("key")
    @classmethod
    def validate_key_format(cls, v: str) -> str:
        """校验配置键格式。

        Parameters
        ----------
        v : str
            待校验的 key 字符串。

        Returns
        -------
        str
            校验通过的 key。

        Raises
        ------
        ValueError
            若 key 格式不符合规范。
        """
        if not _KEY_PATTERN.match(v):
            raise ValueError("key 仅允许大写字母、数字和下划线，且必须以字母开头，长度 1-200")
        return v

    @field_validator("value")
    @classmethod
    def validate_value_vars(cls, v: str) -> str:
        """校验 value 中的 ${VAR} 均为已支持的内置变量。

        Parameters
        ----------
        v : str
            待校验的配置值。

        Returns
        -------
        str
            校验通过的配置值。

        Raises
        ------
        ValueError
            若 value 中存在不受支持的 ${VAR} 占位符。

        Examples
        --------
        >>> AppConfigCreate(key="URL", value="https://${GLOBAL_BASE_URL}/api")
        AppConfigCreate(key='URL', value='https://${GLOBAL_BASE_URL}/api', ...)

        >>> AppConfigCreate(key="URL", value="https://${UNKNOWN_VAR}/api")
        Traceback (most recent call last):
            ...
        ValidationError: ...
        """
        return _validate_interpolation_vars(v)


class AppConfigUpdate(BaseModel):
    """更新配置请求。

    仅允许修改 value / description / is_secret，key 不可变更。

    Parameters
    ----------
    value : str | None
        新配置值（不传则不修改）。
    description : str | None
        新说明（不传则不修改）。
    is_secret : bool | None
        是否敏感值（不传则不修改）。
    """

    value: str | None = Field(default=None, description="新配置值")
    description: str | None = Field(default=None, description="新说明")
    is_secret: bool | None = Field(default=None, description="是否敏感值")

    @field_validator("value")
    @classmethod
    def validate_value_vars(cls, v: str | None) -> str | None:
        """校验 value 中的 ${VAR} 均为已支持的内置变量。

        Parameters
        ----------
        v : str | None
            待校验的配置值，不传时跳过校验。

        Returns
        -------
        str | None
            校验通过的配置值。

        Raises
        ------
        ValueError
            若 value 中存在不受支持的 ${VAR} 占位符。
        """
        if v is None:
            return v
        return _validate_interpolation_vars(v)


class AppConfigListItem(BaseModel):
    """配置列表展示项。

    is_secret=True 时，value 由 Service 层替换为 "***"。

    Attributes
    ----------
    id : str
        配置 ID。
    key : str
        配置键。
    value : str
        配置值（敏感值显示为 ***）。
    description : str | None
        说明。
    is_secret : bool
        是否敏感值。
    updated_at : datetime
        最后更新时间。
    """

    id: str = Field(..., description="配置 ID")
    key: str = Field(..., description="配置键")
    value: str = Field(..., description="配置值（敏感值显示为 ***）")
    description: str | None = Field(default=None, description="说明")
    is_secret: bool = Field(default=False, description="是否敏感值")
    updated_at: datetime = Field(..., description="最后更新时间")


class AppConfigDetail(BaseModel):
    """配置详情（用于编辑回填，返回原始 value 含模板占位符）。

    Attributes
    ----------
    id : str
        配置 ID。
    key : str
        配置键。
    value : str
        配置值（原始模板，含 ${VAR} 占位符）。
    description : str | None
        说明。
    is_secret : bool
        是否敏感值。
    created_at : datetime
        创建时间。
    updated_at : datetime
        最后更新时间。
    """

    id: str = Field(..., description="配置 ID")
    key: str = Field(..., description="配置键")
    value: str = Field(..., description="配置值（原始模板，含 ${VAR} 占位符）")
    description: str | None = Field(default=None, description="说明")
    is_secret: bool = Field(default=False, description="是否敏感值")
    created_at: datetime = Field(..., description="创建时间")
    updated_at: datetime = Field(..., description="最后更新时间")


class AppConfigListResponse(BaseModel):
    """配置列表响应。

    Attributes
    ----------
    items : list[AppConfigListItem]
        配置列表。
    total : int
        总数。
    page : int
        当前页码。
    page_size : int
        每页数量。
    """

    items: list[AppConfigListItem] = Field(default_factory=list, description="配置列表")
    total: int = Field(..., description="总数")
    page: int = Field(..., description="当前页码")
    page_size: int = Field(..., description="每页数量")


class PullConfigsPayload(BaseModel):
    """Webhook pull_configs 请求负载。

    SDK 和 Webhook handler 共用此模型组装请求参数。

    Attributes
    ----------
    keys : list[str] | None
        需要拉取的 key 列表，不传则返回全部。
    context_variables : ContextVariables
        插值上下文变量（平台内置变量实际值）。

    Examples
    --------
    >>> from hezor_common.utilities.config_interpolation import ContextVariables
    >>> payload = PullConfigsPayload(
    ...     keys=["MY_KEY"],
    ...     context_variables=ContextVariables(global_base_url="https://api.hezor.ai"),
    ... )
    """

    keys: list[str] | None = Field(
        default=None,
        description="需要拉取的 key 列表，不传则返回全部配置",
    )
    context_variables: ContextVariables = Field(
        default_factory=ContextVariables,
        description="插值上下文变量（平台内置变量实际值）",
    )


class PullConfigsResponse(BaseModel):
    """Webhook pull_configs 响应数据。

    public 和 user 配置分别返回，key 冲突时 user 配置优先级高于 public。

    Attributes
    ----------
    public : dict[str, str]
        公共配置（管理员维护，经变量插值后的最终值）。
    user : dict[str, str]
        用户私有配置（用户自维护，经变量插值后的最终值）。

    Examples
    --------
    >>> resp = PullConfigsResponse(
    ...     public={"API_BASE": "https://api.hezor.ai"},
    ...     user={"MY_KEY": "user_value"},
    ... )
    >>> resp.merged()
    {'API_BASE': 'https://api.hezor.ai', 'MY_KEY': 'user_value'}
    """

    public: dict[str, str] = Field(
        default_factory=dict,
        description="公共配置（管理员维护，经变量插值后的最终值）",
    )
    user: dict[str, str] = Field(
        default_factory=dict,
        description="用户私有配置（用户自维护，经变量插值后的最终值）",
    )

    def merged(self) -> dict[str, str]:
        """返回合并后的配置字典，用户配置优先于公共配置。

        Returns
        -------
        dict[str, str]
            public + user 合并结果，user 中的 key 覆盖 public 中的同名 key。

        Examples
        --------
        >>> resp = PullConfigsResponse(
        ...     public={"KEY": "public_value", "ONLY_PUBLIC": "x"},
        ...     user={"KEY": "user_value"},
        ... )
        >>> resp.merged()
        {'KEY': 'user_value', 'ONLY_PUBLIC': 'x'}
        """
        return {**self.public, **self.user}


__all__ = [
    "RESERVED_KEY_PREFIXES",
    "SUPPORTED_INTERPOLATION_VARS",
    "AppConfigType",
    "AppConfig",
    "AppConfigCreate",
    "AppConfigUpdate",
    "AppConfigListItem",
    "AppConfigDetail",
    "AppConfigListResponse",
    "PullConfigsPayload",
    "PullConfigsResponse",
]
