from __future__ import annotations

from pathlib import Path
from typing import Iterable, Sequence
from urllib.parse import urlparse

import httpx

from ..interfaces import DocumentDownloader
from ..models import DocumentIngestRequest, DownloadedDocument


class HttpDocumentDownloader(DocumentDownloader):
    """
    Downloads documents over HTTP(S) with basic validation.
    """

    def __init__(
        self,
        *,
        timeout: float = 30.0,
        allowed_mime_types: Iterable[str] | None = None,
        max_bytes: int = 15 * 1024 * 1024,
    ) -> None:
        self._timeout = timeout
        self._allowed_mime_types: Sequence[str] = (
            tuple(allowed_mime_types) if allowed_mime_types else ()
        )
        self._max_bytes = max_bytes

    async def fetch(self, request: DocumentIngestRequest) -> DownloadedDocument:
        async with httpx.AsyncClient(
            follow_redirects=True, timeout=self._timeout
        ) as client:
            response = await client.get(request.source_url)
        response.raise_for_status()

        content = response.content
        if len(content) > self._max_bytes:
            raise ValueError(
                f"Downloaded file exceeds limit of {self._max_bytes} bytes "
                f"(received {len(content)} bytes)."
            )

        content_type = response.headers.get("content-type", "").split(";")[0].strip()
        if self._allowed_mime_types and content_type not in self._allowed_mime_types:
            raise ValueError(
                f"Unsupported mime type '{content_type}'. "
                f"Allowed: {self._allowed_mime_types}"
            )

        parsed = urlparse(request.source_url)
        filename = parsed.path.rsplit("/", 1)[-1] if parsed.path else None
        return DownloadedDocument(
            content=content,
            url=request.source_url,
            mime_type=content_type or request.mime_type,
            filename=filename,
        )


class LocalFileDocumentDownloader(DocumentDownloader):
    """
    Reads document bytes from a local file path.
    """

    def __init__(
        self,
        *,
        max_bytes: int = 15 * 1024 * 1024,
        default_mime_type: str = "application/pdf",
    ) -> None:
        self._max_bytes = max_bytes
        self._default_mime_type = default_mime_type

    async def fetch(self, request: DocumentIngestRequest) -> DownloadedDocument:
        file_path = Path(request.source_url).expanduser().resolve()
        if not file_path.exists():
            raise ValueError(f"File '{file_path}' not found.")

        content = file_path.read_bytes()
        if len(content) > self._max_bytes:
            raise ValueError(
                f"File '{file_path}' exceeds limit of {self._max_bytes} bytes "
                f"(size {len(content)} bytes)."
            )

        return DownloadedDocument(
            content=content,
            url=str(file_path),
            mime_type=request.mime_type or self._default_mime_type,
            filename=file_path.name,
        )
