"""Simple in-memory rate limits for unauthenticated auth endpoints (single-process)."""

from __future__ import annotations

import threading
import time
from collections import defaultdict

_lock = threading.Lock()
_buckets: dict[str, list[float]] = defaultdict(list)

_WINDOW_SEC = 3600.0
_MAX_FORGOT_PER_EMAIL = 5
_MAX_FORGOT_PER_IP = 30
_MAX_RESET_PER_IP = 60
_MAX_RESEND_VERIFY_PER_EMAIL = 5
_MAX_RESEND_VERIFY_PER_IP = 30
_MAX_RESEND_OTP_PER_EMAIL = 5
_MAX_RESEND_OTP_PER_IP = 30


def _prune(ts_list: list[float], now: float) -> None:
    cutoff = now - _WINDOW_SEC
    while ts_list and ts_list[0] < cutoff:
        ts_list.pop(0)


def _hit(key: str, max_hits: int) -> bool:
    """Return True if under limit (request allowed). False if rate limited."""
    now = time.monotonic()
    with _lock:
        bucket = _buckets[key]
        _prune(bucket, now)
        if len(bucket) >= max_hits:
            return False
        bucket.append(now)
        return True


_MAX_LOGIN_PER_EMAIL = 5
_MAX_LOGIN_PER_IP = 10
_LOGIN_WINDOW_SEC = 900.0


def allow_login(email_normalized: str, client_ip: str) -> bool:
    """Return True if under limit (request allowed). False if rate limited."""
    now = time.monotonic()
    with _lock:
        for key, max_hits in (
            (f"login:email:{email_normalized}", _MAX_LOGIN_PER_EMAIL),
            (f"login:ip:{client_ip}", _MAX_LOGIN_PER_IP),
        ):
            bucket = _buckets[key]
            cutoff = now - _LOGIN_WINDOW_SEC
            while bucket and bucket[0] < cutoff:
                bucket.pop(0)
            if len(bucket) >= max_hits:
                return False
        for key in (f"login:email:{email_normalized}", f"login:ip:{client_ip}"):
            _buckets[key].append(now)
        return True


def allow_forgot_password(email_normalized: str, client_ip: str) -> bool:
    if not _hit(f"forgot:email:{email_normalized}", _MAX_FORGOT_PER_EMAIL):
        return False
    if not _hit(f"forgot:ip:{client_ip}", _MAX_FORGOT_PER_IP):
        return False
    return True


def allow_reset_password(client_ip: str) -> bool:
    return _hit(f"reset:ip:{client_ip}", _MAX_RESET_PER_IP)


def allow_resend_verification(email_normalized: str, client_ip: str) -> bool:
    if not _hit(f"resend_verify:email:{email_normalized}", _MAX_RESEND_VERIFY_PER_EMAIL):
        return False
    if not _hit(f"resend_verify:ip:{client_ip}", _MAX_RESEND_VERIFY_PER_IP):
        return False
    return True


def allow_resend_registration_otp(email_normalized: str, client_ip: str) -> bool:
    if not _hit(f"resend_otp:email:{email_normalized}", _MAX_RESEND_OTP_PER_EMAIL):
        return False
    if not _hit(f"resend_otp:ip:{client_ip}", _MAX_RESEND_OTP_PER_IP):
        return False
    return True
