"""
Webhook / HTTP POST sink for extractor.

POSTs each batch of records as a JSON array to an HTTP endpoint.
Useful for custom integrations, serverless pipelines, or any HTTP API.

Auth (env vars):
    EXTRACTOR_WEBHOOK_HEADERS_JSON   (JSON dict of request headers)
    EXTRACTOR_WEBHOOK_TIMEOUT        (request timeout in seconds, default: 30)

No extra packages needed (requests is a core dependency).
"""

from __future__ import annotations

import ipaddress
import json
import logging
import os
import socket
from typing import Any
from urllib.parse import urlparse as _urlparse

from .base import BaseSink
from .registry import register_sink

logger = logging.getLogger(__name__)


def _check_ssrf(url: str) -> None:
    """Block requests to private/internal IP ranges (SSRF protection)."""
    hostname = _urlparse(url).hostname
    if not hostname:
        raise ValueError("SSRF blocked: could not determine hostname from URL")
    try:
        results = socket.getaddrinfo(hostname, None)
    except socket.gaierror as exc:
        raise ValueError(f"SSRF blocked: hostname resolution failed ({exc})") from exc
    _blocked = [
        ipaddress.ip_network("10.0.0.0/8"),
        ipaddress.ip_network("172.16.0.0/12"),
        ipaddress.ip_network("192.168.0.0/16"),
        ipaddress.ip_network("127.0.0.0/8"),
        ipaddress.ip_network("169.254.0.0/16"),
        ipaddress.ip_network("::1/128"),
        ipaddress.ip_network("fe80::/10"),
    ]
    for _family, _type, _proto, _canonname, sockaddr in results:
        ip = ipaddress.ip_address(sockaddr[0])
        if any(ip in net for net in _blocked):
            raise ValueError("SSRF blocked: destination resolves to private/internal IP")


class WebhookSink(BaseSink):
    def __init__(
        self,
        url: str = "",
        batch_size: int = 50,
    ) -> None:
        self.url = url or os.environ.get("EXTRACTOR_WEBHOOK_URL", "")
        self.batch_size = batch_size
        self._buffer: list[dict[str, Any]] = []
        self._total_inserted = 0
        if not self.url:
            raise ValueError("WebhookSink requires a URL. Pass url= or set EXTRACTOR_WEBHOOK_URL.")

    def connect(self) -> None:
        pass  # stateless — connection per request

    def _headers(self) -> dict:
        raw = os.environ.get("EXTRACTOR_WEBHOOK_HEADERS_JSON", "{}")
        try:
            return json.loads(raw)
        except json.JSONDecodeError:
            return {}

    def feed(self, record: dict[str, Any]) -> None:
        self._buffer.append(record)
        if len(self._buffer) >= self.batch_size:
            self.write_batch(self._buffer)
            self._buffer = []

    def write_batch(self, records: list[dict[str, Any]]) -> None:
        if not records:
            return
        _check_ssrf(self.url)
        import requests

        # SSL verification cannot be disabled; hardcoded True for security
        verify = True
        timeout = int(os.environ.get("EXTRACTOR_WEBHOOK_TIMEOUT", "30"))
        headers = {"Content-Type": "application/json", **self._headers()}
        payload = json.dumps(records, ensure_ascii=False).encode()
        resp = requests.post(
            self.url, data=payload, headers=headers, verify=verify, timeout=timeout
        )
        resp.raise_for_status()
        self._total_inserted += len(records)
        logger.debug("WebhookSink: posted %d records to %s", len(records), self.url)

    def flush(self) -> None:
        if self._buffer:
            self.write_batch(self._buffer)
            self._buffer = []

    def close(self) -> None:
        pass

    @property
    def total_inserted(self) -> int:
        return self._total_inserted


register_sink("webhook", WebhookSink)
register_sink("http_post", WebhookSink)
