"""福利模块(钱包 / 签到 / 任务)请求 / 响应 schemas。

约定同 auth:字段 snake_case、时间 ISO 8601(UTC)、金额存整数。
"""
from __future__ import annotations

from datetime import datetime

from pydantic import BaseModel, ConfigDict, Field


# ===== 钱包 / 我的资产 =====

class CoinAccountOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    coin_balance: int = Field(..., description="当前金币余额")
    cash_balance_cents: int = Field(..., description="当前现金余额(分)")
    invite_cash_balance_cents: int = Field(0, description="邀请奖励金余额(分,与现金隔离)")
    total_coin_earned: int = Field(..., description="累计赚取金币")


class CoinTransactionOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    amount: int = Field(..., description="正=入账,负=出账")
    balance_after: int
    biz_type: str
    ref_id: str | None = None
    remark: str | None = None
    created_at: datetime


class CoinTransactionPage(BaseModel):
    items: list[CoinTransactionOut]
    next_cursor: int | None = Field(None, description="下一页游标(末条 id);为空表示到底")


class CashTransactionOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    amount_cents: int = Field(..., description="正=兑入,负=提现")
    balance_after_cents: int
    biz_type: str
    ref_id: str | None = None
    remark: str | None = None
    created_at: datetime


class CashTransactionPage(BaseModel):
    items: list[CashTransactionOut]
    next_cursor: int | None = Field(None, description="下一页游标(末条 id);为空表示到底")


# ===== 金币兑现金 =====

class ExchangeInfoOut(BaseModel):
    coin_per_yuan: int = Field(..., description="多少金币兑 1 元")
    min_coin: int = Field(..., description="单次兑换最少金币")
    step_coin: int = Field(..., description="兑换金币需为该值的整数倍(整分)")


class ExchangeRequest(BaseModel):
    coin_amount: int = Field(..., gt=0, description="要兑换的金币数")


class ExchangeResultOut(BaseModel):
    coin_amount: int = Field(..., description="本次扣除的金币")
    cash_added_cents: int = Field(..., description="本次兑入的现金(分)")
    coin_balance: int = Field(..., description="兑换后金币余额")
    cash_balance_cents: int = Field(..., description="兑换后现金余额(分)")


# ===== 提现(现金 → 微信零钱) =====

class WithdrawInfoOut(BaseModel):
    min_cents: int = Field(..., description="单次最低提现(分)")
    max_cents: int = Field(..., description="单次最高提现(分)")
    wechat_bound: bool = Field(..., description="当前用户是否已绑定微信")
    wechat_nickname: str | None = Field(None, description="微信昵称(可能为空/脱敏)")
    wechat_avatar_url: str | None = Field(None, description="微信头像 URL(可能为空)")
    transfer_auth_enabled: bool = Field(
        False, description="是否已开启免确认到账(开启后提现免跳微信确认,直接到账)"
    )


# ===== 免确认收款授权(用户授权免确认模式)=====

class TransferAuthResultOut(BaseModel):
    already_active: bool = Field(False, description="是否已是开启状态(无需再授权)")
    package_info: str | None = Field(None, description="拉起微信授权页的 package(已开启时为空)")
    mch_id: str | None = None
    app_id: str | None = None


class TransferAuthStatusOut(BaseModel):
    state: str = Field(..., description="none(未开启)/pending(待确认)/active(已开启)/closed(已关闭)")
    enabled: bool = Field(..., description="是否已开启免确认到账")


class BindWechatRequest(BaseModel):
    code: str = Field(..., description="微信授权返回的 code")


class BindWechatResultOut(BaseModel):
    bound: bool = True
    wechat_nickname: str | None = None
    wechat_avatar_url: str | None = None


class UnbindWechatRequest(BaseModel):
    # 有进行中提现单时,首次解绑会被拦(needs_confirm),用户确认后带 force=true 再调一次才真解绑。
    force: bool = Field(False, description="跳过「进行中提现」二次确认,强制解绑")


class UnbindWechatResultOut(BaseModel):
    bound: bool = False
    # 有进行中提现单且未 force → True:本次未解绑(bound 仍 True),客户端弹确认弹窗。
    needs_confirm: bool = False
    message: str | None = None


class WithdrawRequest(BaseModel):
    amount_cents: int = Field(..., gt=0, description="提现金额(分)")
    source: str = Field("coin_cash", description="提现账户:coin_cash(金币现金) / invite_cash(邀请奖励金)")
    user_name: str | None = Field(None, description="实名(达额时微信要求,可空)")
    out_bill_no: str | None = Field(
        None, description="客户端幂等键(商户单号):同号重试不重复转账。不传则服务端生成"
    )
    skip_review: bool = Field(
        False,
        description=(
            "调试直发:跳过人工审核立即打款。仅非生产(APP_ENV!=prod)生效,"
            "客户端仅 debug 包下发(开发设置开关)。生产恒走人工审核。"
        ),
    )


class WithdrawResultOut(BaseModel):
    out_bill_no: str = Field(..., description="商户提现单号(查单用)")
    status: str = Field(..., description="reviewing(待审核) / pending / success / failed / rejected")
    wechat_state: str | None = Field(None, description="微信侧原始状态")
    amount_cents: int
    cash_balance_cents: int = Field(..., description="提现后现金余额(分)")
    # 供 App 拉起微信确认页(WXOpenBusinessView requestMerchantTransfer)
    package_info: str | None = None
    mch_id: str | None = None
    app_id: str | None = None


class WithdrawStatusOut(BaseModel):
    out_bill_no: str
    status: str = Field(..., description="reviewing(待审核) / pending / success / failed / rejected")
    wechat_state: str | None = None
    amount_cents: int
    fail_reason: str | None = Field(None, description="failed/rejected 时的原因(用户可读)")
    # 审核通过进入打款、且微信要求用户确认(WAIT_USER_CONFIRM)时,带回这三项供 App 拉起微信确认页
    package_info: str | None = None
    mch_id: str | None = None
    app_id: str | None = None


class WithdrawOrderOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    out_bill_no: str
    amount_cents: int
    source: str = Field("coin_cash", description="提现账户:coin_cash / invite_cash")
    status: str = Field(..., description="reviewing(待审核) / pending / success / failed / rejected")
    wechat_state: str | None = None
    fail_reason: str | None = None
    created_at: datetime


class WithdrawOrderPage(BaseModel):
    items: list[WithdrawOrderOut]
    next_cursor: int | None = Field(None, description="下一页游标(末条 id);为空表示到底")


# ===== 签到 =====

class SigninStepOut(BaseModel):
    day: int = Field(..., description="循环内第几天 1..14")
    coin: int
    status: str = Field(..., description="claimed / today / locked")


class SigninStatusOut(BaseModel):
    today_signed: bool
    consecutive_days: int
    today_cycle_day: int
    today_coin: int
    can_claim: bool
    steps: list[SigninStepOut]


class SigninResultOut(BaseModel):
    coin_awarded: int
    cycle_day: int
    streak: int
    coin_balance: int = Field(..., description="签到后金币余额")


class SigninBoostRequest(BaseModel):
    ad_ref_id: str | None = Field(None, description="广告会话/交易号。当前开发期可空,后续接 S2S 时回填")


class SigninBoostResultOut(BaseModel):
    coin_awarded: int = Field(..., description="本次膨胀补发金币")
    coin_balance: int = Field(..., description="膨胀补发后金币余额")
    signin_date: str = Field(..., description="被膨胀的签到日期 YYYY-MM-DD")


# ===== 任务 =====

class TaskOut(BaseModel):
    task_key: str
    coin: int
    claimed: bool


class TaskListOut(BaseModel):
    items: list[TaskOut]


class TaskClaimResultOut(BaseModel):
    task_key: str
    coin_awarded: int
    coin_balance: int = Field(..., description="领奖后金币余额")


# ===== 省钱(累计帮你省了 / 省钱战绩 / 明细)=====

class SavingsSummaryOut(BaseModel):
    total_saved_cents: int = Field(..., description="累计省下(分)")
    order_count: int = Field(..., description="累计省钱订单数")
    avg_saved_cents: int = Field(..., description="平均每单省(分)")


class SavingsBattleOut(BaseModel):
    week_saved_cents: int = Field(..., description="本周已省(分)")
    beat_percent: int = Field(..., description="超过百分之多少用户")
    streak_days: int = Field(..., description="连续省钱天数")
    compare_count: int = Field(..., description="累计完成比价次数(= 比价上报记录数)")


class SavingsRecordOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    order_amount_cents: int  # 实付金额(分)
    saved_amount_cents: int  # 省下(分)
    original_price_cents: int | None = None  # 源平台原价(分);demo 行为空
    platform: str | None = None
    title: str | None = None
    shop_name: str | None = None
    dishes: list[str] = []
    pay_channel: str | None = None  # wechat/alipay;demo 行为空
    source_platform_name: str | None = None  # 源平台名,如 美团
    created_at: datetime


class SavingsRecordPage(BaseModel):
    items: list[SavingsRecordOut]
    next_cursor: int | None = Field(None, description="下一页游标(末条 id);为空表示到底")
