"""离线零依赖图形验证码：服务端生成 SVG，答案经 HMAC 签名放入 Cookie，无状态校验"""
import base64
import hashlib
import hmac
import random
import secrets
import string
import time

import config

COOKIE_NAME = "site_captcha"
TTL_SECONDS = 300
CHARS = string.ascii_uppercase.replace("O", "").replace("I", "").replace("L", "") + "23456789"
COLORS = ["#4f6ef7", "#2f9e63", "#e8853d", "#d64550", "#7a5af8", "#0ea5e9"]


def _sign(payload: str) -> str:
    return hmac.new(config.SECRET_KEY.encode(), payload.encode(), hashlib.sha256).hexdigest()[:32]


def _derive_code(nonce: str) -> str:
    """答案由服务端密钥派生，Cookie 里只放 nonce，客户端拿不到答案"""
    digest = hmac.new(config.SECRET_KEY.encode(), ("captcha|" + nonce).encode(), hashlib.sha256).digest()
    return "".join(CHARS[b % len(CHARS)] for b in digest[:4])


def generate():
    """返回 (svg_text, cookie_value)"""
    nonce = secrets.token_hex(8)
    code = _derive_code(nonce)
    expires = int(time.time()) + TTL_SECONDS
    payload = f"{expires}|{nonce}"
    cookie = base64.urlsafe_b64encode(payload.encode()).decode() + "." + _sign(payload)

    rnd = random.Random(int(nonce, 16))
    parts = [
        '<svg xmlns="http://www.w3.org/2000/svg" width="130" height="44" viewBox="0 0 130 44">',
        '<rect width="130" height="44" fill="#f2f4fb" rx="8"/>',
    ]
    for _ in range(4):  # 干扰线
        parts.append(
            f'<line x1="{rnd.randint(0, 30)}" y1="{rnd.randint(0, 44)}" x2="{rnd.randint(100, 130)}" '
            f'y2="{rnd.randint(0, 44)}" stroke="{rnd.choice(COLORS)}" stroke-width="{rnd.randint(1, 2)}" opacity="0.5"/>'
        )
    for _ in range(18):  # 噪点
        parts.append(
            f'<circle cx="{rnd.randint(0, 130)}" cy="{rnd.randint(0, 44)}" r="{rnd.randint(1, 2)}" '
            f'fill="{rnd.choice(COLORS)}" opacity="0.4"/>'
        )
    x = 14
    for ch in code:  # 字符
        rot = rnd.randint(-22, 22)
        y = rnd.randint(28, 34)
        parts.append(
            f'<text x="{x}" y="{y}" font-family="monospace" font-size="{rnd.randint(24, 30)}" '
            f'font-weight="bold" fill="{rnd.choice(COLORS)}" '
            f'transform="rotate({rot} {x} {y})">{ch}</text>'
        )
        x += 28
    parts.append("</svg>")
    return "".join(parts), cookie


def verify(cookie_value, code):
    """校验验证码；通过返回 True（调用方应立即清除 Cookie 实现一次性）"""
    if not cookie_value or not code or "." not in cookie_value:
        return False
    b64, sig = cookie_value.rsplit(".", 1)
    try:
        payload = base64.urlsafe_b64decode(b64.encode()).decode()
    except Exception:
        return False
    if not hmac.compare_digest(_sign(payload), sig):
        return False
    parts = payload.split("|")
    if len(parts) != 2:
        return False
    expires, nonce = parts
    try:
        if int(expires) < int(time.time()):
            return False
    except ValueError:
        return False
    return hmac.compare_digest(_derive_code(nonce), code.strip().upper())
