from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.dependencies import get_db
from app.schemas.sms import SmsResponse, SmsScene, SmsSendRequest, SmsVerifyRequest
from app.services.auth import get_user_by_phone
from app.services.sms import generate_and_send_sms, verify_sms_code

router = APIRouter(prefix="/sms", tags=["短信验证码"])


def _validate_captcha_token(captcha_token: str | None) -> None:
    if settings.SMS_MOCK:
        return

    if not (req_captcha := captcha_token):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="需要验证码",
        )

    from app.services.auth import decode_access_token

    token_sub = decode_access_token(req_captcha)
    if not token_sub or not token_sub.startswith("captcha:"):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="验证码无效",
        )


async def _validate_sms_scene_phone(
    *,
    db: AsyncSession,
    phone: str,
    scene: SmsScene,
) -> None:
    """
    按短信来源场景做手机号注册状态前置校验。

    规则：
    1. login：必须是已注册手机号，否则拦截发送。
    2. register：必须是未注册手机号，否则拦截发送。
    3. common：通用场景，暂不校验手机号注册状态。
    4. set_password：保留旧场景，按通用场景处理，暂不校验手机号注册状态。
    5. 其他未确认场景：理论上会被 SmsScene 枚举拦截；这里额外兜底。
    """
    if scene == SmsScene.login:
        user = await get_user_by_phone(db, phone)
        if not user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="手机号未注册，请先注册",
            )
        return

    if scene == SmsScene.register:
        user = await get_user_by_phone(db, phone)
        if user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="该手机号已注册，请直接登录",
            )
        return

    if scene in {SmsScene.common, SmsScene.set_password}:
        return

    raise HTTPException(
        status_code=status.HTTP_400_BAD_REQUEST,
        detail="未确认短信场景，禁止发送验证码",
    )


@router.post(
    "/send",
    response_model=SmsResponse,
    summary="发送短信验证码",
    description=(
        "客户端发送短信验证码。"
        "scene=register 用于注册，发送前会校验手机号未注册；"
        "scene=login 用于短信登录，发送前会校验手机号已注册；"
        "scene=common 用于通用短信场景，暂不校验手机号注册状态；"
        "scene=set_password 用于设置密码，保留旧场景，暂按通用场景处理。"
        "正式环境会按配置校验图形验证码。"
    ),
)
async def send_sms_code(
    req: SmsSendRequest,
    db: AsyncSession = Depends(get_db),
):
    _validate_captcha_token(req.captcha_token)

    await _validate_sms_scene_phone(
        db=db,
        phone=req.phone,
        scene=req.scene,
    )

    try:
        ok, code = await generate_and_send_sms(req.phone, req.scene.value)
    except ValueError as exc:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(exc),
        ) from exc

    if not ok:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="短信发送失败，请稍后重试",
        )

    message = f"验证码已发送:{code}" if settings.SMS_MOCK else "验证码已发送"
    return SmsResponse(message=message, success=True)


@router.post(
    "/verify",
    response_model=SmsResponse,
    summary="校验短信验证码",
    description="校验指定手机号、场景下的短信验证码。业务接口一般会内部校验，本接口主要用于前端调试或单独校验。",
)
async def verify_sms(req: SmsVerifyRequest):
    ok = await verify_sms_code(req.phone.strip(), req.code.strip(), req.scene.value)
    if not ok:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="验证码错误或已过期",
        )
    return SmsResponse(message="验证成功", success=True)