"""心愿单同步 endpoint。

两个 endpoint:
  - POST /api/v1/wish/list   拉取某 phone 的全部心愿单(含 soft-deleted)
  - POST /api/v1/wish/upsert 批量推送本地修改(新增 / 更新 / 软删)

同步模型:
  - phone 作为账号主键(占坑期)
  - 每条 wish 有 updated_at,冲突解决用 last-write-wins
  - 软删通过 deleted_at != NULL,避免 A 设备删了 B 设备再 push 上来 "复活"
  - 客户端用 client_temp_id 关联自家本地行,服务端在响应里回带 server_id

不做的:
  - 不做 since 增量,占坑期心愿单条目数估计 < 100,全量同步成本低且简单
  - 不做 user_id 单独主键,phone 当主键(后续升级 user_id 时迁移 1 张表)
"""
from __future__ import annotations

import logging
import time
from typing import Optional

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field

from app import db

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

router = APIRouter(prefix="/api/v1/wish")


class WishItem(BaseModel):
    """跟客户端 sync 用的精简 schema。
    matchedClusterId / matchCount / 等本地命中状态不同步(跨设备无意义)。"""
    server_id: Optional[int] = None  # 新增本地条目时为 null
    client_temp_id: Optional[str] = None  # 客户端用于关联响应里的 server_id 回填
    title: str
    target_price: Optional[float] = None
    note: Optional[str] = None
    notify_enabled: bool = True
    created_at: int  # 客户端创建时间(ms)
    updated_at: int  # 最后修改时间(ms),冲突解决用
    deleted_at: Optional[int] = None  # 软删,非 NULL 表示已删


class WishListRequest(BaseModel):
    phone: str = Field(..., min_length=11, max_length=15)


class WishListResponse(BaseModel):
    items: list[WishItem]


class WishUpsertRequest(BaseModel):
    phone: str = Field(..., min_length=11, max_length=15)
    items: list[WishItem]


class WishUpsertItemAck(BaseModel):
    """每条上传 item 的 server-side 处理结果。
    client_temp_id 透传,用于客户端按它找到对应本地行回填 server_id。"""
    client_temp_id: Optional[str]
    server_id: int
    updated_at: int  # 服务端落库后的 updated_at(可能因冲突解决用了 client 提供的也可能用了 server 已有的)
    accepted: bool  # false=服务端有更新的版本,拒绝了本次更新(客户端应保留服务端版本)


class WishUpsertResponse(BaseModel):
    items: list[WishUpsertItemAck]


@router.post("/list", response_model=WishListResponse)
def wish_list(req: WishListRequest) -> WishListResponse:
    """拉取该 phone 的所有 wish(含软删),客户端按 deleted_at 自行过滤显示。"""
    with db.get_conn() as conn:
        rows = conn.execute(
            """
            SELECT id, title, target_price, note, notify_enabled,
                   created_at, updated_at, deleted_at
            FROM wish_item
            WHERE phone = ?
            ORDER BY updated_at DESC
            """,
            (req.phone,),
        ).fetchall()

    items = [
        WishItem(
            server_id=r["id"],
            client_temp_id=None,
            title=r["title"],
            target_price=r["target_price"],
            note=r["note"],
            notify_enabled=bool(r["notify_enabled"]),
            created_at=r["created_at"],
            updated_at=r["updated_at"],
            deleted_at=r["deleted_at"],
        )
        for r in rows
    ]
    logger.info("wish_list phone=%s count=%d", _mask(req.phone), len(items))
    return WishListResponse(items=items)


@router.post("/upsert", response_model=WishUpsertResponse)
def wish_upsert(req: WishUpsertRequest) -> WishUpsertResponse:
    """批量 upsert。每条:
       - server_id 为 null → INSERT,返回新 id
       - server_id 不为 null → 检查 phone 归属 + 比较 updated_at
         - 客户端 updated_at > 服务端 → UPDATE,accepted=true
         - 否则 → 拒绝,返回服务端最新数据(accepted=false)
    """
    acks: list[WishUpsertItemAck] = []
    with db.get_conn() as conn:
        for it in req.items:
            if it.server_id is None:
                cur = conn.execute(
                    """
                    INSERT INTO wish_item
                      (phone, title, target_price, note, notify_enabled,
                       created_at, updated_at, deleted_at)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                    """,
                    (
                        req.phone, it.title, it.target_price, it.note,
                        1 if it.notify_enabled else 0,
                        it.created_at, it.updated_at, it.deleted_at,
                    ),
                )
                acks.append(WishUpsertItemAck(
                    client_temp_id=it.client_temp_id,
                    server_id=int(cur.lastrowid),
                    updated_at=it.updated_at,
                    accepted=True,
                ))
                continue

            # server_id 非空:先查现状,做 last-write-wins
            row = conn.execute(
                "SELECT phone, updated_at FROM wish_item WHERE id = ?",
                (it.server_id,),
            ).fetchone()
            if row is None or row["phone"] != req.phone:
                # 没找到 / 不属于该 phone:拒绝(防越权)
                logger.warning(
                    "wish_upsert reject phone=%s server_id=%s (not found or phone mismatch)",
                    _mask(req.phone), it.server_id,
                )
                acks.append(WishUpsertItemAck(
                    client_temp_id=it.client_temp_id,
                    server_id=it.server_id,
                    updated_at=row["updated_at"] if row else it.updated_at,
                    accepted=False,
                ))
                continue
            if it.updated_at <= row["updated_at"]:
                # 服务端有更新或同样的版本,拒绝;客户端应该按服务端版本同步过去
                acks.append(WishUpsertItemAck(
                    client_temp_id=it.client_temp_id,
                    server_id=it.server_id,
                    updated_at=row["updated_at"],
                    accepted=False,
                ))
                continue
            conn.execute(
                """
                UPDATE wish_item SET
                  title = ?, target_price = ?, note = ?, notify_enabled = ?,
                  updated_at = ?, deleted_at = ?
                WHERE id = ? AND phone = ?
                """,
                (
                    it.title, it.target_price, it.note,
                    1 if it.notify_enabled else 0,
                    it.updated_at, it.deleted_at,
                    it.server_id, req.phone,
                ),
            )
            acks.append(WishUpsertItemAck(
                client_temp_id=it.client_temp_id,
                server_id=it.server_id,
                updated_at=it.updated_at,
                accepted=True,
            ))

    logger.info(
        "wish_upsert phone=%s in=%d accepted=%d",
        _mask(req.phone), len(req.items), sum(1 for a in acks if a.accepted),
    )
    return WishUpsertResponse(items=acks)


def _mask(phone: str) -> str:
    return (phone[:3] + "****" + phone[-2:]) if len(phone) >= 5 else "***"
