"""Base adapter interface for all external API integrations.

All ingestion adapters follow the same contract:
1. Fetch external payloads for a given ticker/source config.
2. Return a structured result with raw bytes, parsed items, and metadata.
3. The ingestion worker handles MinIO upload, PostgreSQL metadata, and downstream job emission.

Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 3.1, 3.2, 3.3, 3.4
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any

from services.shared.storage import _prefixed


@dataclass
class AdapterResult:
    """Result of a single adapter fetch operation."""

    source_type: str
    ticker: str
    items: list[dict[str, Any]]
    raw_payload: bytes
    content_hash: str
    fetched_at: datetime
    error: str | None = None
    # HTTP metadata for observability
    http_status: int | None = None
    response_time_ms: float | None = None
    # Additional metadata the adapter wants to pass downstream
    metadata: dict[str, Any] = field(default_factory=dict)

    @property
    def ok(self) -> bool:
        """True if the fetch succeeded without error."""
        return self.error is None and len(self.items) > 0

    @property
    def item_count(self) -> int:
        return len(self.items)


class BaseAdapter(ABC):
    """Interface for all ingestion adapters.

    Subclasses implement fetch() for their specific API and source_type()
    to identify the adapter class. The ingestion worker orchestrates
    persistence and downstream job emission.
    """

    @abstractmethod
    async def fetch(self, ticker: str, config: dict[str, Any]) -> AdapterResult:
        """Fetch data for a given ticker using source config.

        Args:
            ticker: The company ticker symbol.
            config: Source-specific configuration from the sources table.

        Returns:
            AdapterResult with raw payload, parsed items, and metadata.
        """
        ...

    @abstractmethod
    def source_type(self) -> str:
        """Return the source type identifier for this adapter (e.g. 'market_api')."""
        ...

    def bucket_name(self) -> str:
        """Return the MinIO bucket name for raw artifact storage.

        Override in subclasses if the bucket differs from the default pattern.
        """
        return _prefixed(f"stonks-raw-{self.source_type().replace('_api', '').replace('_', '-')}")

    def artifact_path(self, ticker: str, document_id: str, now: datetime) -> str:
        """Build the MinIO object path for a raw artifact.

        Pattern: /{source_type}/{ticker}/{yyyy}/{mm}/{dd}/{document_id}/raw.json
        """
        return (
            f"{self.source_type()}/{ticker}/"
            f"{now.strftime('%Y/%m/%d')}/{document_id}/raw.json"
        )
