"""
HTTP/HTTPS source connector — single file download.

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

Auth:
    EXTRACTOR_HTTP_HEADERS_JSON   (JSON dict of headers, e.g. {"Authorization": "Bearer <token>"})

URI format:
    https://example.com/document.pdf
    http://internal.corp/report.docx
"""

from __future__ import annotations

import contextlib
import ipaddress
import json
import os
import socket
import tempfile
from collections.abc import Iterator
from pathlib import Path
from urllib.parse import urlparse

from .base import SourceFile
from .registry import register_source

_BLOCKED_NETWORKS = [
    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"),
]


def _check_ssrf(url: str) -> None:
    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
    for _family, _type, _proto, _canonname, sockaddr in results:
        ip = ipaddress.ip_address(sockaddr[0])
        if any(ip in net for net in _BLOCKED_NETWORKS):
            raise ValueError("SSRF blocked: destination resolves to private/internal IP")


_DOWNLOAD_CHUNK = 65_536
_MAX_BYTES_DEFAULT = 500 * 1024 * 1024  # 500 MB


class HttpSource:
    scheme = "http"

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

    def _verify_ssl(self) -> bool:
        # SSL verification cannot be disabled; use EXTRACTOR_HTTP_CA_BUNDLE for custom CAs (not yet implemented)
        return True

    def list(self, uri: str, glob_pattern: str | None = None) -> Iterator[SourceFile]:
        """HTTP source only supports single-file URIs — yields one SourceFile."""
        import requests

        _check_ssrf(uri)
        resp = requests.head(
            uri,
            headers=self._headers(),
            verify=self._verify_ssl(),
            timeout=30,
            allow_redirects=False,
        )
        resp.raise_for_status()
        filename = uri.split("/")[-1].split("?")[0] or "download"
        size = int(resp.headers.get("Content-Length", 0)) or None
        etag = resp.headers.get("ETag", "").strip('"') or None
        last_mod = resp.headers.get("Last-Modified")
        ct = resp.headers.get("Content-Type", "").split(";")[0]
        yield SourceFile(
            uri=uri,
            filename=filename,
            size_bytes=size,
            etag=etag,
            last_modified=last_mod,
            content_type=ct,
        )

    @contextlib.contextmanager
    def fetch(self, source_file: SourceFile, tmp_dir: Path | None = None) -> Iterator[Path]:
        import requests

        _check_ssrf(source_file.uri)
        suffix = Path(source_file.filename).suffix or ".bin"
        with tempfile.NamedTemporaryFile(suffix=suffix, dir=tmp_dir, delete=False) as tmp:
            tmp_path = Path(tmp.name)
        try:
            max_bytes = int(os.environ.get("EXTRACTOR_HTTP_MAX_BYTES", _MAX_BYTES_DEFAULT))
            with requests.get(
                source_file.uri,
                headers=self._headers(),
                verify=self._verify_ssl(),
                stream=True,
                timeout=60,
                allow_redirects=False,
            ) as resp:
                resp.raise_for_status()
                downloaded = 0
                with open(tmp_path, "wb") as f:
                    for chunk in resp.iter_content(chunk_size=_DOWNLOAD_CHUNK):
                        downloaded += len(chunk)
                        if downloaded > max_bytes:
                            raise ValueError(
                                f"Download exceeded EXTRACTOR_HTTP_MAX_BYTES ({max_bytes})"
                            )
                        f.write(chunk)
            yield tmp_path
        finally:
            with contextlib.suppress(FileNotFoundError):
                tmp_path.unlink()


class HttpsSource(HttpSource):
    scheme = "https"


register_source(HttpSource())
register_source(HttpsSource())
