"""极验 v4 二次校验。

策略：
- 未配置 captcha_id 时直接放行（本地开发友好）
- 极验自身异常（超时、5xx）按文档建议视为通过，不阻断业务
- 极验明确返回 fail 时阻断
"""
import hashlib
import hmac
import logging

import aiohttp
from fastapi import HTTPException

from app.config import settings

log = logging.getLogger("qycw.captcha")

_REQUEST_TIMEOUT = aiohttp.ClientTimeout(total=5)


def _handle_provider_unavailable(message: str) -> None:
    if settings.GEETEST_FAIL_OPEN:
        log.warning("%s, fail-open enabled", message)
        return
    log.warning("%s, rejecting captcha", message)
    raise HTTPException(status_code=400, detail="人机验证服务暂时不可用，请稍后重试")


def _sign(lot_number: str) -> str:
    return hmac.new(
        settings.GEETEST_CAPTCHA_KEY.encode(),
        lot_number.encode(),
        digestmod=hashlib.sha256,
    ).hexdigest()


async def verify(payload: dict | None) -> None:
    """payload: {lot_number, captcha_output, pass_token, gen_time}。失败抛 400。"""
    if not settings.GEETEST_CAPTCHA_ID:
        return

    if not payload or not all(
        payload.get(k) for k in ("lot_number", "captcha_output", "pass_token", "gen_time")
    ):
        raise HTTPException(status_code=400, detail="请先完成人机验证")

    lot_number = payload["lot_number"]
    form = {
        "lot_number": lot_number,
        "captcha_output": payload["captcha_output"],
        "pass_token": payload["pass_token"],
        "gen_time": payload["gen_time"],
        "sign_token": _sign(lot_number),
    }
    url = f"{settings.GEETEST_API_SERVER}/validate?captcha_id={settings.GEETEST_CAPTCHA_ID}"

    try:
        async with aiohttp.ClientSession(timeout=_REQUEST_TIMEOUT) as sess:
            async with sess.post(url, data=form) as resp:
                if resp.status != 200:
                    _handle_provider_unavailable(f"geetest non-200: {resp.status}")
                    return
                body = await resp.json(content_type=None)
    except Exception as e:
        _handle_provider_unavailable(f"geetest request failed: {e}")
        return

    if body.get("result") != "success":
        reason = body.get("reason") or "人机验证未通过"
        log.info("geetest reject lot=%s reason=%s", lot_number, reason)
        raise HTTPException(status_code=400, detail=f"人机验证失败：{reason}")
