"""厂商推送(测试/联调)接口契约。与消息中心同族,出参统一 camelCase。"""
from __future__ import annotations

from typing import Any

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


class _CamelModel(BaseModel):
    model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)


class PushVendorStatus(_CamelModel):
    vendor: str = Field(description="厂商 key:honor / huawei / xiaomi / oppo / vivo")
    label: str = Field(description="厂商中文名")
    configured: bool = Field(description="服务端凭据是否齐全(齐全才能真发,mock 不受影响)")
    missing_keys: list[str] = Field(description="缺失的 .env 配置键;configured=true 时为空")


class PushVendorsOut(_CamelModel):
    vendors: list[PushVendorStatus] = Field(description="5 个厂商的配置状态")


class PushTemplateOut(_CamelModel):
    type: str = Field(description="通知类型 key(13 种,与消息中心 type 一致)")
    category: str = Field(description="分类 key")
    category_label: str = Field(description="分类中文标签")
    card_style: str = Field(description="站内卡片版式")
    push_title: str = Field(description="push 标题(≤11 字固定文案,PRD §5)")
    push_body_sample: str = Field(description="push 正文示例(模板用 PRD 示例值渲染后的效果)")
    push_body_template: str = Field(description="push 正文模板原文,{var} 为变量占位")
    variables: list[str] = Field(description="模板变量名列表(调 /push/test 时可在 vars 里覆盖)")
    sample_vars: dict[str, str] = Field(description="各变量的 PRD 示例值(vars 未覆盖时的缺省)")


class PushTemplatesOut(_CamelModel):
    templates: list[PushTemplateOut] = Field(description="13 种通知类型的 push 模板(PRD 编号顺序)")


class PushTestRequest(_CamelModel):
    """POST /api/v1/push/test 入参。三种发送内容来源(优先级从高到低):

    1. 直接指定 title + content;
    2. 指定 type(13 种之一)→ 按 PRD §5 模板渲染,vars 可覆盖模板变量;
    3. 都不传 → 发一条通用测试文案。

    推送目标:pushToken 直填,或 deviceId 反查该用户已注册设备(/api/v1/device/register 上报过的)。
    """

    vendor: str = Field(
        default="",
        description="厂商:honor/huawei/xiaomi/oppo/vivo(中文「华为」「小米」等别名也识别)。"
                    "留空时用 deviceId 对应设备上报的 push_vendor",
    )
    push_token: str = Field(default="", description="厂商 push token / regId;留空则走 deviceId 反查")
    device_id: str = Field(default="", description="设备 id(客户端 DeviceId.get());用于反查 token")
    type: str = Field(
        default="",
        description="通知类型 key(13 种,见 GET /push/templates);留空且未直接给 title/content 时发通用测试文案",
    )
    vars: dict[str, str] = Field(
        default_factory=dict,
        description="覆盖 push 模板变量,如 {\"coins\":\"520\",\"cash\":\"6.66\"};缺省用 PRD 示例值",
    )
    title: str = Field(default="", description="直接指定标题(优先于 type 模板)")
    content: str = Field(default="", description="直接指定正文(优先于 type 模板)")
    create_notification: bool = Field(
        default=False,
        description="true=同时往该用户的消息中心 mock 列表插入一条同类型未读通知,push extras 带上它的"
                    " notificationId → 可闭环验证「点 push → 落地 → 调 /notifications/read 消红点」联动"
                    "(仅 type 为 13 种类型之一时生效)",
    )
    mock: bool = Field(
        default=True,
        description="true(默认)=不真调厂商 API,返回渲染结果(联调安全);false=真发,要求该厂商凭据已配置",
    )


class PushTestOut(_CamelModel):
    ok: bool = Field(description="发送(或 mock 渲染)成功")
    mock: bool = Field(description="本次是否 mock(未真调厂商 API)")
    vendor: str = Field(description="实际使用的厂商 key(已归一化)")
    title: str = Field(description="实际下发的 push 标题")
    body: str = Field(description="实际下发的 push 正文")
    extras: dict[str, str] = Field(
        description="随 push 下发的自定义键值(客户端深链用):type 必有;createNotification=true 时带"
                    " notificationId 及该通知的业务参数(feedbackId / permission / …)"
    )
    notification_id: int | None = Field(
        default=None, description="createNotification=true 时新插入的站内 mock 通知 id"
    )
    missing_keys: list[str] = Field(
        default_factory=list,
        description="该厂商仍缺失的配置键(mock 发送时提示「真发前还需配什么」;真发时必为空)",
    )
    vendor_response: dict[str, Any] | None = Field(
        default=None, description="真发时厂商 API 的原始响应(mock 时为 null)"
    )
