"""Facade that sanitizes adapter output and revalidates every discovered URL."""

from __future__ import annotations

import logging
from urllib.parse import urldefrag

from app.adapters.base import MediaCandidate, ParsedPage, SiteAdapter, VideoPageLink
from app.errors import AppError
from app.services.caption_parser import normalize_text
from app.services.url_validator import NetworkGuard

logger = logging.getLogger(__name__)


class Extractor:
    def __init__(self, guard: NetworkGuard) -> None:
        self.guard = guard

    @staticmethod
    def _short_url(url: str) -> str:
        return url[:120]

    def extract(self, adapter: SiteAdapter, html: str, url: str) -> ParsedPage:
        parsed = adapter.parse(html, url)
        if url not in {parsed.url}:
            parsed.url = url
        title = normalize_text(parsed.title)[:500] or "ویدئو"
        caption = normalize_text(parsed.caption)[:20_000] or title
        videos = self._sanitize_videos(parsed.videos, title, caption)
        video_pages = self._sanitize_video_pages(parsed.video_pages, url)
        next_url = self._sanitize_next(parsed.next_page_url, url)
        return ParsedPage(
            url=url,
            title=title,
            caption=caption,
            videos=videos,
            video_pages=video_pages,
            next_page_url=next_url,
            next_page_strategy=parsed.next_page_strategy,
            caption_source=parsed.caption_source,
            adapter_name=adapter.name,
        )

    def _allowed(self, candidate: str | None) -> str | None:
        if not candidate:
            return None
        try:
            return self.guard.validate_host_only(candidate).url
        except AppError as exc:
            logger.debug("discarding discovered URL (%s): %s", type(exc).__name__, str(exc)[:160])
            return None
        except Exception:
            return None

    def _sanitize_videos(
        self,
        candidates: list[MediaCandidate],
        page_title: str,
        page_caption: str,
    ) -> list[MediaCandidate]:
        result: list[MediaCandidate] = []
        seen: set[str] = set()
        for candidate in candidates:
            media_url = self._allowed(candidate.media_url)
            source_url = self._allowed(candidate.source_url)
            page_url = self._allowed(candidate.page_url)
            if media_url is None and source_url is None:
                continue
            identity = source_url or media_url
            if identity is None or identity in seen:
                continue
            seen.add(identity)
            title = normalize_text(candidate.title)[:500] or page_title
            caption = normalize_text(candidate.caption)[:20_000] or page_caption or title
            result.append(
                MediaCandidate(
                    page_url=page_url or candidate.page_url,
                    source_url=identity,
                    title=title,
                    caption=caption,
                    media_url=media_url,
                    thumbnail_url=self._allowed(candidate.thumbnail_url),
                    duration_seconds=candidate.duration_seconds,
                    media_type=candidate.media_type or "video/unknown",
                    file_size=candidate.file_size,
                    source=candidate.source,
                )
            )
        return result

    def _sanitize_video_pages(
        self, links: list[VideoPageLink], current_url: str
    ) -> list[VideoPageLink]:
        result: list[VideoPageLink] = []
        seen = {urldefrag(current_url)[0]}
        for link in links:
            url = self._allowed(link.url)
            if not url or url in seen:
                continue
            seen.add(url)
            result.append(
                VideoPageLink(
                    url=url,
                    title=normalize_text(link.title)[:500],
                    caption=normalize_text(link.caption)[:20_000],
                    thumbnail_url=self._allowed(link.thumbnail_url),
                    source=link.source,
                )
            )
        return result

    def _sanitize_next(self, next_url: str | None, current_url: str) -> str | None:
        if not next_url:
            return None
        cleaned = self._allowed(next_url)
        if not cleaned:
            return None
        if urldefrag(cleaned)[0] == urldefrag(current_url)[0]:
            return None
        return cleaned
