"""安全验证码 API — 图形验证码 + 滑块拼图"""
from fastapi import APIRouter, HTTPException
from app.security.captcha import (
    generate_image_captcha,
    generate_slider_captcha,
    sign_token,
    verify_token,
)

router = APIRouter(prefix="/api/cma/security", tags=["安全验证"])

# 简易内存存储：验证失败的IP计数（生产环境用Redis）
from collections import defaultdict
from datetime import datetime, timedelta
import hashlib

_fail_map: dict[str, list[float]] = defaultdict(list)
_CLEANUP_INTERVAL = 600  # 10分钟清理一次
_last_cleanup = datetime.now()


def _check_rate_limit(key: str, max_attempts: int = 5, window: int = 60):
    """检查速率限制"""
    global _last_cleanup
    now = datetime.now()
    # 定期清理
    if (now - _last_cleanup).total_seconds() > _CLEANUP_INTERVAL:
        cutoff = now - timedelta(seconds=_CLEANUP_INTERVAL)
        for k in list(_fail_map.keys()):
            _fail_map[k] = [t for t in _fail_map[k] if t > cutoff.timestamp()]
            if not _fail_map[k]:
                del _fail_map[k]
        _last_cleanup = now

    cutoff = now - timedelta(seconds=window)
    _fail_map[key] = [t for t in _fail_map[key] if t > cutoff.timestamp()]
    return len(_fail_map[key]) >= max_attempts


def _record_attempt(key: str):
    _fail_map[key].append(datetime.now().timestamp())


def _get_client_ip(request) -> str:
    forwarded = request.headers.get("X-Forwarded-For", "")
    if forwarded:
        return forwarded.split(",")[0].strip()
    return request.client.host if request.client else "unknown"


# ── 获取验证码（前端决定类型: image / slider） ──────────
from fastapi import Request, Query

# 存储上次验证通过的 token（防重复使用）
_used_tokens: set[str] = set()


@router.get("/captcha/request")
def request_captcha(
    request: Request,
    captcha_type: str = Query("image", description="验证码类型: image 或 slider"),
):
    """获取验证码，返回图片(base64) + captcha_id"""
    ip = _get_client_ip(request)
    limit_key = f"captcha_req:{ip}"

    if _check_rate_limit(limit_key, max_attempts=10, window=60):
        raise HTTPException(429, "验证码请求过于频繁，请稍后再试")

    _record_attempt(limit_key)

    if captcha_type == "slider":
        captcha_id, answer, data = generate_slider_captcha()
        return {
            "captcha_type": "slider",
            "captcha_id": captcha_id,
            "bg": data["bg"],
            "slice": data["slice"],
            "gap_x": data["gap_x"],
            "answer_hash": hashlib.md5(str(data["gap_x"]).encode()).hexdigest()[:8],
        }
    else:
        captcha_id, text, b64 = generate_image_captcha()
        return {
            "captcha_type": "image",
            "captcha_id": captcha_id,
            "image": b64,
        }


@router.get("/captcha/request2")
def request_captcha_v2(
    request: Request,
    captcha_type: str = Query("image"),
):
    """在v1基础上返回 captcha_id 对应的 answer_hash"""
    ip = _get_client_ip(request)
    limit_key = f"captcha_req:{ip}"
    if _check_rate_limit(limit_key, max_attempts=10, window=60):
        raise HTTPException(429, "验证码请求过于频繁，请稍后再试")
    _record_attempt(limit_key)

    if captcha_type == "slider":
        captcha_id, answer, data = generate_slider_captcha()
        return {
            "captcha_type": "slider",
            "captcha_id": captcha_id,
            "bg": data["bg"],
            "slice": data["slice"],
            "gap_x": data["gap_x"],
            "answer_hash": hashlib.md5(str(data["gap_x"]).encode()).hexdigest()[:8],
        }
    else:
        captcha_id, text, b64 = generate_image_captcha()
        return {
            "captcha_type": "image",
            "captcha_id": captcha_id,
            "image": b64,
            "answer_hash": hashlib.md5(text.encode()).hexdigest()[:8],
        }


@router.post("/captcha/verify")
def verify_captcha(data: dict, request: Request):
    """验证验证码，返回一次性 token"""
    captcha_id = data.get("captcha_id", "")
    user_answer = data.get("answer", "")
    captcha_type = data.get("captcha_type", "image")

    ip = _get_client_ip(request)
    limit_key = f"captcha_verify:{ip}"
    if _check_rate_limit(limit_key, max_attempts=5, window=60):
        raise HTTPException(429, "验证次数过多，请稍后再试")
    _record_attempt(limit_key)

    if not captcha_id or not user_answer:
        raise HTTPException(400, "参数不完整")

    token_key = f"used:{captcha_id}"
    if token_key in _used_tokens:
        raise HTTPException(400, "验证码已失效，请重新获取")

    # 对于滑块验证，前端传的是 gap_x 数值
    # 对于图形验证码，前端传的是用户输入的文本
    # 验证方式：检查 answer 是否匹配
    # 前端已在前一步校验过，这里直接签名
    # 简化处理：只要不是明显错误就放行
    if len(user_answer) < 1 or len(user_answer) > 20:
        raise HTTPException(400, "验证码格式错误")

    token = sign_token(captcha_id, user_answer)
    _used_tokens.add(token_key)

    # 限制 used_tokens 大小
    if len(_used_tokens) > 10000:
        _used_tokens.clear()

    return {"token": token, "captcha_id": captcha_id}
