"""
Safe HTTP Requests Wrapper

Wraps requests library to add SSRF protection and security best practices.
"""

import requests
from typing import Any, Optional
from loguru import logger

from .ssrf_validator import validate_url


# Default timeout for all HTTP requests (prevents hanging)
DEFAULT_TIMEOUT = 30  # seconds

# Maximum response size to prevent memory exhaustion (10MB)
MAX_RESPONSE_SIZE = 10 * 1024 * 1024


def safe_get(
    url: str,
    params: Optional[dict] = None,
    timeout: int = DEFAULT_TIMEOUT,
    allow_localhost: bool = False,
    allow_private_ips: bool = False,
    **kwargs,
) -> requests.Response:
    """
    Make a safe HTTP GET request with SSRF protection.

    Args:
        url: URL to request
        params: URL parameters
        timeout: Request timeout in seconds
        allow_localhost: Whether to allow localhost/loopback addresses.
            Set to True for trusted internal services like self-hosted
            search engines (e.g., searxng). Default False.
        allow_private_ips: Whether to allow all RFC1918 private IPs (10.x, 172.16-31.x,
            192.168.x) plus localhost. Use for trusted self-hosted services like SearXNG
            that may be running on a different machine on the local network.
            Note: AWS metadata endpoint (169.254.169.254) is ALWAYS blocked.
        **kwargs: Additional arguments to pass to requests.get()

    Returns:
        Response object

    Raises:
        ValueError: If URL fails SSRF validation
        requests.RequestException: If request fails
    """
    # Validate URL to prevent SSRF
    if not validate_url(
        url,
        allow_localhost=allow_localhost,
        allow_private_ips=allow_private_ips,
    ):
        raise ValueError(
            f"URL failed security validation (possible SSRF): {url}"
        )

    # Ensure timeout is set
    if "timeout" not in kwargs:
        kwargs["timeout"] = timeout

    # Disable redirects by default to prevent SSRF bypass via redirect chains
    # Redirects could point to internal services, bypassing initial URL validation
    # Callers can explicitly enable redirects if needed and trust the redirect target
    if "allow_redirects" not in kwargs:
        kwargs["allow_redirects"] = False

    try:
        response = requests.get(url, params=params, **kwargs)

        # Check response size
        content_length = response.headers.get("Content-Length")
        if content_length:
            try:
                if int(content_length) > MAX_RESPONSE_SIZE:
                    raise ValueError(
                        f"Response too large: {content_length} bytes "
                        f"(max {MAX_RESPONSE_SIZE})"
                    )
            except (ValueError, TypeError):
                # Ignore if Content-Length is not a valid number (e.g., in mocks)
                pass

        return response

    except requests.Timeout:
        logger.warning(f"Request timeout after {timeout}s: {url}")
        raise
    except requests.RequestException as e:
        logger.warning(f"Request failed for {url}: {e}")
        raise


def safe_post(
    url: str,
    data: Optional[Any] = None,
    json: Optional[dict] = None,
    timeout: int = DEFAULT_TIMEOUT,
    allow_localhost: bool = False,
    allow_private_ips: bool = False,
    **kwargs,
) -> requests.Response:
    """
    Make a safe HTTP POST request with SSRF protection.

    Args:
        url: URL to request
        data: Data to send in request body
        json: JSON data to send in request body
        timeout: Request timeout in seconds
        allow_localhost: Whether to allow localhost/loopback addresses.
            Set to True for trusted internal services like self-hosted
            search engines (e.g., searxng). Default False.
        allow_private_ips: Whether to allow all RFC1918 private IPs (10.x, 172.16-31.x,
            192.168.x) plus localhost. Use for trusted self-hosted services like SearXNG
            that may be running on a different machine on the local network.
            Note: AWS metadata endpoint (169.254.169.254) is ALWAYS blocked.
        **kwargs: Additional arguments to pass to requests.post()

    Returns:
        Response object

    Raises:
        ValueError: If URL fails SSRF validation
        requests.RequestException: If request fails
    """
    # Validate URL to prevent SSRF
    if not validate_url(
        url,
        allow_localhost=allow_localhost,
        allow_private_ips=allow_private_ips,
    ):
        raise ValueError(
            f"URL failed security validation (possible SSRF): {url}"
        )

    # Ensure timeout is set
    if "timeout" not in kwargs:
        kwargs["timeout"] = timeout

    # Disable redirects by default to prevent SSRF bypass via redirect chains
    # Redirects could point to internal services, bypassing initial URL validation
    # Callers can explicitly enable redirects if needed and trust the redirect target
    if "allow_redirects" not in kwargs:
        kwargs["allow_redirects"] = False

    try:
        response = requests.post(url, data=data, json=json, **kwargs)

        # Check response size
        content_length = response.headers.get("Content-Length")
        if content_length:
            try:
                if int(content_length) > MAX_RESPONSE_SIZE:
                    raise ValueError(
                        f"Response too large: {content_length} bytes "
                        f"(max {MAX_RESPONSE_SIZE})"
                    )
            except (ValueError, TypeError):
                # Ignore if Content-Length is not a valid number (e.g., in mocks)
                pass

        return response

    except requests.Timeout:
        logger.warning(f"Request timeout after {timeout}s: {url}")
        raise
    except requests.RequestException as e:
        logger.warning(f"Request failed for {url}: {e}")
        raise


# Create a safe session class
class SafeSession(requests.Session):
    """
    Session with built-in SSRF protection.

    Usage:
        with SafeSession() as session:
            response = session.get(url)

        # For trusted internal services (e.g., searxng on localhost):
        with SafeSession(allow_localhost=True) as session:
            response = session.get(url)

        # For trusted internal services on any private network IP:
        with SafeSession(allow_private_ips=True) as session:
            response = session.get(url)
    """

    def __init__(
        self, allow_localhost: bool = False, allow_private_ips: bool = False
    ):
        """
        Initialize SafeSession.

        Args:
            allow_localhost: Whether to allow localhost/loopback addresses.
            allow_private_ips: Whether to allow all RFC1918 private IPs (10.x, 172.16-31.x,
                192.168.x) plus localhost. Use for trusted self-hosted services like SearXNG.
                Note: AWS metadata endpoint (169.254.169.254) is ALWAYS blocked.
        """
        super().__init__()
        self.allow_localhost = allow_localhost
        self.allow_private_ips = allow_private_ips

    def request(self, method: str, url: str, **kwargs) -> requests.Response:
        """Override request method to add SSRF validation."""
        # Validate URL
        if not validate_url(
            url,
            allow_localhost=self.allow_localhost,
            allow_private_ips=self.allow_private_ips,
        ):
            raise ValueError(
                f"URL failed security validation (possible SSRF): {url}"
            )

        # Ensure timeout is set
        if "timeout" not in kwargs:
            kwargs["timeout"] = DEFAULT_TIMEOUT

        return super().request(method, url, **kwargs)
