"""Adapter contracts and normalized crawl data structures."""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass, field

from bs4 import BeautifulSoup


@dataclass(slots=True)
class VideoPageLink:
    """An explicitly advertised page that may contain a video."""

    url: str
    title: str = ""
    caption: str = ""
    thumbnail_url: str | None = None
    source: str = "adapter"


@dataclass(slots=True)
class MediaCandidate:
    page_url: str
    source_url: str
    title: str
    caption: str | None
    media_url: str | None
    thumbnail_url: str | None = None
    duration_seconds: float | None = None
    media_type: str = "video"
    file_size: int | None = None
    source: str = "generic"


@dataclass(slots=True)
class ParsedPage:
    url: str
    title: str
    caption: str
    videos: list[MediaCandidate] = field(default_factory=list)
    video_pages: list[VideoPageLink] = field(default_factory=list)
    next_page_url: str | None = None
    next_page_strategy: str = "none"
    caption_source: str = "unknown"
    adapter_name: str = "generic"


class SiteAdapter(ABC):
    """Base class for a supported site.

    The generic adapter is a safe fallback for direct media, standard metadata
    and structured data. For listing pages that use site-specific markup,
    implement a separate adapter and provide selectors or override methods.
    """

    name: str = "base"
    # Set to True only when the site legitimately needs JavaScript rendering.
    render_with_browser: bool = False
    title_selectors: tuple[str, ...] = ()
    caption_selectors: tuple[str, ...] = ()
    video_page_selectors: tuple[str, ...] = ()
    next_page_selectors: tuple[str, ...] = ()
    # Optional explicit "load more" button; clicked a bounded number of times.
    load_more_selector: str | None = None
    max_load_more_clicks: int = 0
    scroll_loads: int = 0
    wait_for_selector: str | None = None

    @abstractmethod
    def supports(self, url: str) -> bool:
        """Return True if this adapter is appropriate for the URL."""

    @abstractmethod
    def parse(self, html: str, url: str) -> ParsedPage:
        """Return normalized page data. URLs are revalidated by the extractor."""

    def get_next_page_url(self, soup: BeautifulSoup, current_url: str) -> str | None:
        """Site-approved URL generation (for example an API-backed pager).

        Only implementations with explicit knowledge of a site's pattern should
        override this. The generic adapter returns None.
        """
        return None
