from __future__ import annotations

"""Core ingestion interfaces and utilities.

The goal is to keep ingestion modular and source-agnostic. Each ingestor
implements :class:`BaseIngestor` and can be registered with the dispatcher
without affecting others.
"""

from dataclasses import dataclass
from typing import Protocol
from urllib.parse import urlparse
import os

from ..core.models import RawDocument, SourceType


class IngestionError(RuntimeError):
    """Raised when a source cannot be ingested."""


class BaseIngestor(Protocol):
    """Protocol for all ingestors.

    Implementations should be side-effect free apart from reading from the
    given source.
    """

    def supports(self, source: str) -> bool:  # pragma: no cover - simple predicate
        """Return ``True`` if this ingestor knows how to handle ``source``."""

    def ingest(self, source: str) -> RawDocument:
        """Load the content from ``source`` and wrap it as ``RawDocument``."""


@dataclass
class GuessResult:
    """Best-effort guess for a source type.

    This is provided as a helper and is not required for ingestors, but
    many ingestors may find it convenient to use.
    """

    source_type: SourceType


def guess_source_type(source: str) -> GuessResult:
    """Heuristically guess a :class:`SourceType` for ``source``.

    - Existing filesystem paths are treated as :class:`SourceType.FILE`.
    - ``http``/``https`` URLs are treated as :class:`SourceType.WEB`.
    - Everything else is considered :class:`SourceType.TEXT`.
    """

    if os.path.exists(source):
        return GuessResult(source_type=SourceType.FILE)

    parsed = urlparse(source)
    if parsed.scheme in {"http", "https"}:
        return GuessResult(source_type=SourceType.WEB)

    return GuessResult(source_type=SourceType.TEXT)

