import ipaddress
import logging
import os
from fastapi import Request
from slowapi import Limiter

logger = logging.getLogger(__name__)

TRUST_PROXY_HEADERS = os.getenv("TRUST_PROXY_HEADERS", "false").lower() == "true"

# Comma-separated list of CIDR ranges. Only requests whose direct peer is
# inside one of these ranges are allowed to override the client IP via
# X-Forwarded-For / X-Real-IP. Defaults to RFC1918 + loopback for safety —
# operators behind public-internet load balancers must set this explicitly.
_DEFAULT_TRUSTED_PROXIES = "127.0.0.1/32,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
_trusted_proxy_cidrs: list = []
for raw in os.getenv("TRUSTED_PROXY_CIDRS", _DEFAULT_TRUSTED_PROXIES).split(","):
    raw = raw.strip()
    if not raw:
        continue
    try:
        _trusted_proxy_cidrs.append(ipaddress.ip_network(raw, strict=False))
    except ValueError:
        logger.warning("Ignoring invalid TRUSTED_PROXY_CIDRS entry: %s", raw)


def _peer_is_trusted_proxy(peer_ip: str) -> bool:
    try:
        addr = ipaddress.ip_address(peer_ip)
    except ValueError:
        return False
    return any(addr in net for net in _trusted_proxy_cidrs)


def get_client_ip(request: Request) -> str:
    """
    Resolve client IP. Honour reverse-proxy headers only when the direct
    peer is itself a trusted proxy — otherwise the client could spoof
    X-Forwarded-For to bypass rate limiting on /auth/login etc.
    """
    peer = request.client.host if request.client else None

    if TRUST_PROXY_HEADERS and peer and _peer_is_trusted_proxy(peer):
        xff = request.headers.get("x-forwarded-for")
        if xff:
            # Rightmost untrusted hop = leftmost entry the trusted proxy
            # received. Take the first non-trusted IP walking from the right.
            for candidate in reversed([h.strip() for h in xff.split(",") if h.strip()]):
                if not _peer_is_trusted_proxy(candidate):
                    return candidate
            # All hops were trusted proxies — fall back to leftmost.
            return xff.split(",")[0].strip()
        xri = request.headers.get("x-real-ip")
        if xri:
            return xri.strip()

    return peer or "unknown"


limiter = Limiter(key_func=get_client_ip)
