"""邀请相关 API 收发模型。"""
from __future__ import annotations

from datetime import datetime

from pydantic import BaseModel, Field


class InviteInfoOut(BaseModel):
    invite_code: str                # 我的邀请码
    share_url: str                  # 落地页链接(含 ?ref=),前端据此生成二维码 + 复制分享
    invited_count: int              # 已成功邀请人数
    coins_earned: int               # 累计从邀请获得的金币(v1 口径;v2 邀请人改发奖励金)
    reward_balance_cents: int = 0   # v2 可提现邀请奖励金(分)
    reward_withdrawn_cents: int = 0 # v2 累计提现成功的邀请奖励金(分)
    countdown_days_left: int = 7            # v2 本轮剩余天数(7 天 1 轮)
    countdown_is_fresh_round: bool = False  # 是否刚进入新一轮(非首轮第1天)
    countdown_text: str = ""                # 倒计时展示文案(前端直接显示,新轮含换行)


class LandingTrackIn(BaseModel):
    """落地页 dl.html 访问时上报的指纹(用于剪贴板归因失败时兜底)。"""
    ref: str = Field(..., min_length=4, max_length=16, description="邀请码(落地页 ?ref=)")
    screen: str = Field("", max_length=32, description="屏幕分辨率,如 1080x2400")
    # IP / UA 服务端从 HTTP 头自动拿,无需 JS 上报


class LandingTrackOut(BaseModel):
    status: str                     # ok / invalid_code / no_ip


class FingerprintIn(BaseModel):
    """客户端登录后剪贴板归因失败时上报的设备指纹。

    跨端匹配字段:浏览器落地页存的同名字段 == 客户端 Build.MODEL / DisplayMetrics 算的值。
    IP 服务端从 HTTP 头拿,不在这里。
    """
    screen: str = Field("", max_length=32)
    device_model: str = Field("", max_length=64)


class BindInviteIn(BaseModel):
    # invite_code: 可选 — 走指纹兜底时为空(channel=fingerprint)
    invite_code: str | None = Field(
        None, max_length=16, description="邀请码;走指纹兜底时为空"
    )
    channel: str = Field(
        "clipboard", max_length=16,
        description="归因来源:clipboard(首启读剪贴板自动) / manual(手动填) / fingerprint(指纹兜底)",
    )
    # 当 invite_code 为空、channel=fingerprint 时,后端用这组指纹反查
    fingerprint: FingerprintIn | None = None


class BindInviteOut(BaseModel):
    status: str                     # success / already_bound / invalid_code / self_invite / not_eligible / fp_not_found
    coins_awarded: int              # 本次给当前用户(被邀请人)发的金币
    message: str                    # 给前端直接展示的文案


class InviteeItem(BaseModel):
    """被邀请人列表的一条(邀请页小窗 / 完整列表页共用)。

    display_name / avatar_url 的"降级兜底"在后端算好(见 repositories/invite.get_invitees):
    名字 = 昵称 → 微信昵称 → 脱敏手机号(前端拿不到完整号,必须后端脱敏);
    头像 = 用户头像 → 微信头像 → null(前端拿到 null 画默认色块)。
    """
    display_name: str               # 已兜底好的显示名(真名/脱敏号)
    avatar_url: str | None = None   # 头像 URL;null = 前端画默认色块
    coins: int                      # 这次邀请给我(邀请人)发的金币
    invited_at: datetime            # 邀请绑定时间(前端转"今天/3天前")
    is_compared: bool = False       # 该好友是否已完成过一次比价(好友列表分"去提醒/邀请成功";在途列表只取 False)


class InviteeListOut(BaseModel):
    """GET /invite/invitees 响应:被邀请人列表 + 分页。"""
    items: list[InviteeItem]
    total: int                      # 我邀请的总人数(用于"共 N 人")
    has_more: bool                  # 还有下一页吗(完整列表页滚到底加载更多)
