"""积分中心：做任务赚积分、积分提现到余额。"""
import math
import random
from datetime import date, datetime, timezone

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.core.deps import get_current_user
from app.database import get_db
from app.models import (
    AgentSettlement,
    BalanceChangeType,
    BalanceLog,
    PointChangeType,
    PointReward,
    PointRewardStatus,
    PointTask,
    PointTaskType,
    PointWithdrawal,
    PointWithdrawalStatus,
    User,
)
from app.schemas.common import ApiResponse
from app.schemas.user import GeetestPayload
from app.services import captcha_service, membership_service, system_setting_service

router = APIRouter()


class TaskAnswerRequest(BaseModel):
    answer: str = Field(..., min_length=1, max_length=128)


class CheckinRequest(BaseModel):
    captcha: GeetestPayload | None = None


class WithdrawRequest(BaseModel):
    points: int = Field(..., ge=1)


def _normalize_answer(value: str) -> str:
    return value.strip()


def _today() -> date:
    return datetime.now(timezone.utc).date()


async def _membership_points(db: AsyncSession, user_id: int) -> int:
    membership = await membership_service.get_or_create_membership(db, user_id)
    return int(membership.available_points or 0)


@router.get("/center", response_model=ApiResponse)
async def points_center(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """积分中心首页数据：积分余额、任务完成情况、提现配置。"""
    settings = await system_setting_service.get_business_settings(db)
    membership = await membership_service.get_or_create_membership(db, current_user.id)

    result = await db.execute(
        select(PointTask).where(PointTask.user_id == current_user.id)
    )
    tasks = {t.task_type: t for t in result.scalars().all()}

    today = _today()
    today_checkin = None
    for t in tasks.values():
        if t.task_type == PointTaskType.DAILY_CHECKIN and t.checkin_date == today:
            today_checkin = t.checkin_points
            break

    recent_withdrawals = await db.execute(
        select(PointWithdrawal)
        .where(PointWithdrawal.user_id == current_user.id)
        .order_by(PointWithdrawal.id.desc())
        .limit(10)
    )

    self_purchase_res = await db.execute(
        select(func.count(PointReward.id), func.coalesce(func.sum(PointReward.points), 0))
        .where(
            PointReward.user_id == current_user.id,
            PointReward.status.in_([PointRewardStatus.PENDING.value, PointRewardStatus.CLAIMABLE.value]),
        )
    )
    self_purchase_count, self_purchase_points = self_purchase_res.one() or (0, 0)

    referral_res = await db.execute(
        select(func.count(AgentSettlement.id), func.coalesce(func.sum(AgentSettlement.points_amount), 0))
        .where(
            AgentSettlement.user_id == current_user.id,
            AgentSettlement.settlement_type == "member_commission",
            AgentSettlement.status.in_(["pending", "frozen"]),
        )
    )
    referral_count, referral_points = referral_res.one() or (0, 0)

    agent_profit_res = await db.execute(
        select(func.count(AgentSettlement.id), func.coalesce(func.sum(AgentSettlement.points_amount), 0))
        .where(
            AgentSettlement.user_id == current_user.id,
            AgentSettlement.settlement_type == "agent_profit",
            AgentSettlement.status.in_(["pending", "frozen"]),
        )
    )
    agent_profit_count, agent_profit_points = agent_profit_res.one() or (0, 0)

    return ApiResponse(data={
        "available_points": membership.available_points,
        "balance_cents": current_user.balance,
        "qq_rewarded": PointTaskType.QQ_COMMUNITY in tasks,
        "bilibili_rewarded": PointTaskType.BILIBILI_FOLLOW in tasks,
        "today_checkin": today_checkin,
        "self_purchase": {"count": int(self_purchase_count or 0), "points": int(self_purchase_points or 0)},
        "member_commission": {"count": int(referral_count or 0), "points": int(referral_points or 0)},
        "agent_profit": {"count": int(agent_profit_count or 0), "points": int(agent_profit_points or 0)},
        "settings": {
            "qq_reward": settings.qq_reward,
            "bilibili_reward": settings.bilibili_reward,
            "checkin_min_points": settings.checkin_min_points,
            "checkin_max_points": settings.checkin_max_points,
            "withdraw_fee_rate": settings.withdraw_fee_rate,
            "withdraw_min_points": settings.withdraw_min_points,
        },
        "recent_withdrawals": [
            {
                "id": w.id,
                "points_amount": w.points_amount,
                "fee_points": w.fee_points,
                "credited_cents": w.credited_cents,
                "status": w.status.value if hasattr(w.status, "value") else str(w.status),
                "created_at": w.created_at.isoformat() if w.created_at else None,
            }
            for w in recent_withdrawals.scalars().all()
        ],
    })


async def _claim_task(
    db: AsyncSession,
    current_user: User,
    task_type: PointTaskType,
    submitted_answer: str,
    reward_points: int,
    correct_answer: str,
    checkin_date: date | None = None,
    checkin_points: int | None = None,
) -> dict:
    """通用任务完成逻辑：校验答案、去重、发积分。"""
    if reward_points <= 0:
        raise HTTPException(status_code=400, detail="该任务当前未开放")

    normalized_user = _normalize_answer(submitted_answer)
    normalized_correct = _normalize_answer(correct_answer)
    if normalized_user != normalized_correct:
        raise HTTPException(status_code=400, detail="答案不正确，请核对后重试")

    if task_type != PointTaskType.DAILY_CHECKIN:
        existing = await db.scalar(
            select(PointTask.id).where(
                PointTask.user_id == current_user.id,
                PointTask.task_type == task_type,
            )
        )
        if existing:
            raise HTTPException(status_code=400, detail="您已完成过该任务")
    else:
        existing = await db.scalar(
            select(PointTask.id).where(
                PointTask.user_id == current_user.id,
                PointTask.task_type == PointTaskType.DAILY_CHECKIN,
                PointTask.checkin_date == checkin_date,
            )
        )
        if existing:
            raise HTTPException(status_code=400, detail="今日已签到")

    await membership_service.add_points(
        db=db,
        user_id=current_user.id,
        delta=reward_points,
        change_type=PointChangeType.ACTIVITY_GIFT,
        ref_no=f"task:{task_type.value}",
        remark=f"完成积分任务 {task_type.value} 奖励 {reward_points} 积分",
    )

    task = PointTask(
        user_id=current_user.id,
        task_type=task_type,
        reward_points=reward_points,
        submitted_value=normalized_user or None,
        checkin_date=checkin_date,
        checkin_points=checkin_points,
        completed_at=datetime.now(timezone.utc),
    )
    db.add(task)
    await db.flush()

    available_points = await _membership_points(db, current_user.id)
    return {
        "reward_points": reward_points,
        "available_points": available_points,
    }


@router.post("/tasks/qq", response_model=ApiResponse)
async def complete_qq_task(
    body: TaskAnswerRequest,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """完成加入QQ群任务。"""
    settings = await system_setting_service.get_business_settings(db)
    data = await _claim_task(
        db=db,
        current_user=current_user,
        task_type=PointTaskType.QQ_COMMUNITY,
        submitted_answer=body.answer,
        reward_points=settings.qq_reward,
        correct_answer=settings.qq_answer,
    )
    return ApiResponse(data=data)


@router.post("/tasks/bilibili", response_model=ApiResponse)
async def complete_bilibili_task(
    body: TaskAnswerRequest,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """完成关注B站任务。"""
    settings = await system_setting_service.get_business_settings(db)
    data = await _claim_task(
        db=db,
        current_user=current_user,
        task_type=PointTaskType.BILIBILI_FOLLOW,
        submitted_answer=body.answer,
        reward_points=settings.bilibili_reward,
        correct_answer=settings.bilibili_answer,
    )
    return ApiResponse(data=data)


@router.post("/tasks/checkin", response_model=ApiResponse)
async def daily_checkin(
    body: CheckinRequest,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """每日签到，需要极验验证码。"""
    await captcha_service.verify(body.captcha.model_dump() if body.captcha else None)

    settings = await system_setting_service.get_business_settings(db)
    min_p = min(settings.checkin_min_points, settings.checkin_max_points)
    max_p = max(settings.checkin_min_points, settings.checkin_max_points)
    reward_points = random.randint(min_p, max_p)
    today = _today()

    data = await _claim_task(
        db=db,
        current_user=current_user,
        task_type=PointTaskType.DAILY_CHECKIN,
        submitted_answer="",
        reward_points=reward_points,
        correct_answer="",
        checkin_date=today,
        checkin_points=reward_points,
    )
    return ApiResponse(data=data)


@router.post("/withdraw", response_model=ApiResponse)
async def withdraw_points(
    body: WithdrawRequest,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """积分提现到余额，2% 手续费，最低门槛可配置。"""
    settings = await system_setting_service.get_business_settings(db)
    if body.points < settings.withdraw_min_points:
        raise HTTPException(
            status_code=400,
            detail=f"最低提现门槛为 {settings.withdraw_min_points} 积分",
        )

    membership = await membership_service.get_or_create_membership(db, current_user.id)
    if membership.available_points < body.points:
        raise HTTPException(status_code=400, detail="积分不足")

    # 手续费按积分计算（万分比）
    fee_points = math.ceil(body.points * settings.withdraw_fee_rate / 10000)
    net_points = body.points - fee_points
    if net_points <= 0:
        raise HTTPException(status_code=400, detail="提现金额扣除手续费后不足")

    # 1000 积分 = 100 分（1元）
    credited_cents = (net_points // membership_service.POINTS_TO_CENTS_RATE) * 100
    if credited_cents <= 0:
        raise HTTPException(status_code=400, detail="提现积分不足以兑换余额")

    # 扣除积分
    await membership_service.add_points(
        db=db,
        user_id=current_user.id,
        delta=-body.points,
        change_type=PointChangeType.POINTS_USE,
        ref_no=f"withdraw:{current_user.id}:{datetime.now(timezone.utc).timestamp()}",
        remark=f"积分提现 {body.points} 积分，手续费 {fee_points} 积分",
    )

    # 到账余额
    user = await db.get(User, current_user.id)
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
    user.balance += credited_cents
    balance_after = user.balance

    balance_log = BalanceLog(
        user_id=current_user.id,
        change_type=BalanceChangeType.ADMIN_ADJUST,
        delta=credited_cents,
        balance_after=balance_after,
        remark=f"积分提现到账：{body.points} 积分扣除 {fee_points} 积分手续费",
    )
    db.add(balance_log)

    withdrawal = PointWithdrawal(
        user_id=current_user.id,
        points_amount=body.points,
        fee_points=fee_points,
        credited_cents=credited_cents,
        status=PointWithdrawalStatus.COMPLETED,
        processed_at=datetime.now(timezone.utc),
        remark="积分提现到余额",
    )
    db.add(withdrawal)
    await db.flush()

    return ApiResponse(data={
        "available_points": membership.available_points,
        "balance_cents": balance_after,
        "points_amount": body.points,
        "fee_points": fee_points,
        "credited_cents": credited_cents,
    })
