"""Cloudflare Turnstile verification for public auth requests."""

from __future__ import annotations

import httpx
import structlog

from src.config import get_settings
from src.core.exceptions import ForbiddenError

logger = structlog.get_logger()


async def verify_turnstile(token: str | None, *, action: str, remote_ip: str | None) -> None:
    """Verify a single-use Turnstile token before sending an auth email."""
    settings = get_settings()
    if not settings.turnstile_secret.strip():
        if settings.is_dev:
            return
        logger.error("turnstile_configuration_missing", setting="TURNSTILE_SECRET")
        raise ForbiddenError("Bot protection is temporarily unavailable.")
    if not token or len(token) > 2048:
        raise ForbiddenError("Please complete the security check and try again.")

    expected_hosts = {
        host.strip().lower()
        for host in settings.turnstile_expected_hostnames.split(",")
        if host.strip()
    }
    if not expected_hosts:
        logger.error("turnstile_configuration_missing", setting="TURNSTILE_EXPECTED_HOSTNAMES")
        raise ForbiddenError("Bot protection is temporarily unavailable.")
    payload = {"secret": settings.turnstile_secret, "response": token}
    if remote_ip:
        payload["remoteip"] = remote_ip
    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.post(
                "https://challenges.cloudflare.com/turnstile/v0/siteverify", data=payload
            )
            response.raise_for_status()
            result = response.json()
    except (httpx.HTTPError, ValueError):
        raise ForbiddenError("Unable to verify the security check. Please try again.") from None
    if not isinstance(result, dict):
        raise ForbiddenError("Unable to verify the security check. Please try again.")
    if (
        result.get("success") is not True
        or result.get("action") != action
        or str(result.get("hostname", "")).lower() not in expected_hosts
    ):
        raise ForbiddenError("Please complete the security check and try again.")
