"""厂商推送 测试/联调 endpoint。

路由前缀 `/api/v1/push`,需 Bearer 鉴权。围绕「消息中心 13 类通知的厂商直推」提供三件套:
  GET  /vendors    5 个厂商(荣耀/华为/小米/OPPO/vivo)服务端凭据配置状态,缺哪些键一目了然
  GET  /templates  13 种通知类型的 push 标题/正文模板 + PRD 示例渲染效果
  POST /test       测试发送:默认 mock(不真调厂商 API,回显渲染结果);mock=false 真发到手机

与 `/api/v1/device/push-test`(无障碍召回通道的延迟自测)互补:本组面向消息中心 13 类
push 的文案/参数/厂商通道联调。真实业务触发统一走 services/notification_events
(提现回执/反馈审核/爆料通过/好友下单已接入),底层与本测试端点同一条
integrations.vendor_push.send_notification 发送链路。
"""
from __future__ import annotations

import logging

from fastapi import APIRouter, HTTPException, status

from app.api.deps import CurrentUser, DbSession
from app.core import notification_catalog as catalog
from app.integrations import vendor_push
from app.repositories import device as device_repo
from app.repositories import notification as notif_repo
from app.schemas.push import (
    PushTemplateOut,
    PushTemplatesOut,
    PushTestOut,
    PushTestRequest,
    PushVendorsOut,
    PushVendorStatus,
)

logger = logging.getLogger("shagua.push")

router = APIRouter(prefix="/api/v1/push", tags=["push"])

# /vendors 的展示顺序(荣耀/华为/小米/OPPO/vivo)
_VENDOR_ORDER = ("honor", "huawei", "xiaomi", "oppo", "vivo")

_GENERIC_TEST_TITLE = "傻瓜比价测试推送"
_GENERIC_TEST_BODY = "这是一条{label}通道的测试推送,收到说明服务端 → {label}厂商通道已打通。"


@router.get("/vendors", response_model=PushVendorsOut, summary="厂商推送配置状态")
def vendor_status(user: CurrentUser) -> PushVendorsOut:
    """检查 5 个厂商的服务端推送凭据是否配齐(读 .env,不打厂商接口)。

    missingKeys 列出的即还需要在 .env 里补的配置键;全空说明该厂商随时可真发。
    mock 测试(POST /test 默认模式)不依赖任何凭据。
    """
    return PushVendorsOut(
        vendors=[
            PushVendorStatus(
                vendor=v,
                label=vendor_push.VENDOR_LABELS[v],
                configured=not vendor_push.missing_settings(v),
                missing_keys=vendor_push.missing_settings(v),
            )
            for v in _VENDOR_ORDER
        ]
    )


@router.get("/templates", response_model=PushTemplatesOut, summary="13 类通知的 push 模板预览")
def push_templates(user: CurrentUser) -> PushTemplatesOut:
    """PRD §5 的 13 条 push 文案模板 + 用示例值渲染后的效果,联调对文案用。

    标题固定(≤11 字不带变量);正文里 {var} 为变量,POST /test 的 vars 字段可覆盖。
    """
    templates: list[PushTemplateOut] = []
    for key, ntype in catalog.TYPES.items():
        title, body_sample = catalog.render_push(key)
        templates.append(
            PushTemplateOut(
                type=key,
                category=ntype.category,
                category_label=catalog.category_label(ntype.category),
                card_style=ntype.card_style,
                push_title=title,
                push_body_sample=body_sample,
                push_body_template=ntype.push_body_template,
                variables=catalog.push_variable_names(key),
                sample_vars=ntype.sample_vars,
            )
        )
    return PushTemplatesOut(templates=templates)


@router.post("/test", response_model=PushTestOut, summary="测试发送厂商推送(默认 mock)")
def send_test_push(req: PushTestRequest, user: CurrentUser, db: DbSession) -> PushTestOut:
    """向指定厂商 token(或本用户已注册设备)发一条测试 push。

    - **mock=true(默认)**:不真调厂商 API——校验参数、渲染文案后原样返回,并在
      missingKeys 里提示真发前还缺哪些配置。虚拟数据阶段随便打,不会骚扰真机。
    - **mock=false**:真发。要求该厂商凭据已配置(缺则 400 报缺失键);厂商 API 报错回 502。
      注意 vivo 未上架前是测试推送模式(VIVO_PUSH_MODE=1),目标手机要先在 vivo 后台加为测试设备。
    - **createNotification=true**:同时往消息中心(notification 表)插一条同类型未读通知并把
      notificationId 放进 push extras → 客户端点击 push 后调 POST /notifications/read
      {ids:[notificationId]} 即可闭环验证 PRD §4 的 push 已读联动。
    """
    # ---- 1. 解析推送目标(vendor + token):直填优先,缺则按 deviceId 反查已注册设备 ----
    vendor_raw = req.vendor.strip()
    push_token = req.push_token.strip()
    if (not vendor_raw or not push_token) and req.device_id.strip():
        device = device_repo.get_device(db, user_id=user.id, device_id=req.device_id.strip())
        if device is not None:
            vendor_raw = vendor_raw or (device.push_vendor or "")
            push_token = push_token or (device.push_token or "")

    vendor = vendor_push.normalize_vendor(vendor_raw)
    if not vendor or vendor not in vendor_push.SUPPORTED_VENDORS:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"vendor 无效或无法从设备推断,支持: {', '.join(_VENDOR_ORDER)}",
        )
    if not push_token:
        # mock 模式给个占位 token,让「只想看看渲染结果」的调用免造数据;真发必须给真 token。
        if req.mock:
            push_token = "mock-token"
        else:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail="push token 未知:请直传 pushToken,或先用该设备调 /api/v1/device/register 上报",
            )

    # ---- 2. 组装文案与 extras:直填 > type 模板 > 通用测试文案 ----
    extras: dict[str, str] = {}
    notification_id: int | None = None
    if req.type:
        try:
            title, body = catalog.render_push(req.type, req.vars or None)
        except catalog.UnknownNotificationType as e:
            raise HTTPException(status_code=400, detail=str(e)) from e
        extras["type"] = req.type
        if req.create_notification:
            item = notif_repo.insert_sample(db, user.id, req.type)
            notification_id = item.id
            extras.update({str(k): str(v) for k, v in item.extra.items()})
            extras["notificationId"] = str(item.id)
    else:
        label = vendor_push.VENDOR_LABELS[vendor]
        title = _GENERIC_TEST_TITLE
        body = _GENERIC_TEST_BODY.format(label=label)
        extras["type"] = "push_test"
    if req.title.strip():
        title = req.title.strip()
    if req.content.strip():
        body = req.content.strip()

    # ---- 3. 发送(mock / 真发) ----
    missing = vendor_push.missing_settings(vendor)
    vendor_response = None
    if req.mock:
        vendor_push.send_notification(
            vendor, push_token, title=title, body=body, extras=extras, mock=True
        )
    else:
        if missing:
            raise HTTPException(
                status_code=400,
                detail=f"{vendor_push.VENDOR_LABELS[vendor]}推送凭据未配置,先在 .env 补上: "
                       f"{', '.join(missing)}",
            )
        try:
            vendor_response = vendor_push.send_notification(
                vendor, push_token, title=title, body=body, extras=extras
            )
        except vendor_push.VendorPushError as e:
            raise HTTPException(
                status_code=status.HTTP_502_BAD_GATEWAY, detail=f"厂商推送失败: {e}"
            ) from e

    logger.info(
        "push test user_id=%d vendor=%s type=%s mock=%s notification_id=%s",
        user.id, vendor, req.type or "generic", req.mock, notification_id,
    )
    return PushTestOut(
        ok=True,
        mock=req.mock,
        vendor=vendor,
        title=title,
        body=body,
        extras=extras,
        notification_id=notification_id,
        missing_keys=missing,
        vendor_response=vendor_response,
    )
