"""
HTML Content Downloader for web pages.

Downloads and extracts clean text content from HTML web pages.
Extraction is handled by the shared pipeline in extraction/pipeline.py.
"""

from typing import Optional, Dict, Any
from urllib.parse import urlparse
from loguru import logger
from bs4 import BeautifulSoup

from .base import BaseDownloader, ContentType, DownloadResult
from .extraction.pipeline import extract_content_with_metadata
from ...constants import BROWSER_USER_AGENT
from ...security import sanitize_error_for_client


class HTMLDownloader(BaseDownloader):
    """Downloader for HTML web pages - extracts clean text content."""

    def __init__(
        self,
        timeout: int = 30,
        language: str = "English",
        **kwargs,
    ):
        super().__init__(timeout)
        self.session.headers.update({"User-Agent": BROWSER_USER_AGENT})
        self.language = language

    def can_handle(self, url: str) -> bool:
        """
        Check if this downloader can handle the given URL.

        Returns True for any HTTP/HTTPS URL (fallback downloader for web content).
        """
        try:
            parsed = urlparse(url)
            return parsed.scheme in ("http", "https")
        except Exception:
            return False

    def download(
        self, url: str, content_type: ContentType = ContentType.TEXT
    ) -> Optional[bytes]:
        """
        Download and extract text content from HTML page.

        Args:
            url: The URL to download
            content_type: Type of content (TEXT for HTML extraction)

        Returns:
            Extracted text as UTF-8 bytes, or None if failed
        """
        if content_type == ContentType.PDF:
            logger.warning(f"HTML downloader cannot download PDFs: {url}")
            return None

        try:
            html_content = self._fetch_html(url)
            if not html_content:
                return None

            extracted = self._extract_content(html_content, url)
            if extracted:
                text = self._format_extracted_content(extracted)
                return text.encode("utf-8")

            return None

        except Exception:
            logger.exception(f"Failed to download HTML from {url}")
            return None

    def download_with_result(
        self, url: str, content_type: ContentType = ContentType.TEXT
    ) -> DownloadResult:
        """Download content and return detailed result with skip reason."""
        if content_type == ContentType.PDF:
            return DownloadResult(
                skip_reason="HTML downloader does not support PDF downloads"
            )

        try:
            html_content = self._fetch_html(url)
            if not html_content:
                return DownloadResult(
                    skip_reason="Failed to fetch HTML content from URL"
                )

            extracted = self._extract_content(html_content, url)
            if not extracted:
                return DownloadResult(
                    skip_reason="Could not extract meaningful content from page"
                )

            text = self._format_extracted_content(extracted)
            if not text.strip():
                return DownloadResult(skip_reason="Extracted content is empty")

            return DownloadResult(
                content=text.encode("utf-8"),
                is_success=True,
            )

        except Exception as e:
            logger.exception(f"Failed to download HTML from {url}")
            # skip_reason propagates to the browser via the download SSE
            # stream; the fetch URL can carry credentials — scrub before
            # returning (full detail stays in the server log above).
            return DownloadResult(
                skip_reason=sanitize_error_for_client(f"Error: {str(e)}")
            )

    def _fetch_html(self, url: str) -> Optional[str]:
        """Fetch raw HTML content from URL."""
        logger.debug(f"Static fetch: {url}")
        domain = urlparse(url).netloc
        engine_type = f"html_download_{domain}"

        wait_time = self.rate_tracker.apply_rate_limit(engine_type)

        try:
            response = self.session.get(
                url,
                timeout=self.timeout,
                allow_redirects=True,
            )

            if response.status_code == 200:
                content_type = response.headers.get("content-type", "").lower()
                if (
                    "text/html" in content_type
                    or "application/xhtml" in content_type
                ):
                    self.rate_tracker.record_outcome(
                        engine_type=engine_type,
                        wait_time=wait_time,
                        success=True,
                        retry_count=1,
                        search_result_count=1,
                    )
                    return response.text
                logger.warning(
                    f"Unexpected content type for HTML download: {content_type}"
                )
                return None
            logger.warning(f"HTTP {response.status_code} fetching {url}")
            self.rate_tracker.record_outcome(
                engine_type=engine_type,
                wait_time=wait_time,
                success=False,
                retry_count=1,
                error_type=f"HTTP_{response.status_code}",
            )
            return None

        except Exception as e:
            logger.exception(f"Error fetching HTML from {url}")
            self.rate_tracker.record_outcome(
                engine_type=engine_type,
                wait_time=wait_time,
                success=False,
                retry_count=1,
                error_type=type(e).__name__,
            )
            return None

    def _extract_content(self, html: str, url: str) -> Optional[Dict[str, Any]]:
        """Extract clean content and metadata from HTML.

        Delegates to the shared extraction pipeline which handles
        trafilatura, readability, justext, and metadata enrichment.
        """
        try:
            result = extract_content_with_metadata(html, language=self.language)
            if not result:
                return None

            title = result.get("title")
            content = result["content"]

            logger.info(
                f"Extracted {len(content)} chars from {url} "
                f"(title: {title[:50] + '...' if title and len(title) > 50 else title})"
            )
            return {
                "title": title,
                "description": result.get("description"),
                "content": content,
                "url": url,
            }

        except Exception:
            logger.exception("Error extracting content from HTML")
            return None

    def _format_extracted_content(self, extracted: Dict[str, Any]) -> str:
        """Format extracted content as readable text."""
        parts = []

        if extracted.get("title"):
            parts.append(f"# {extracted['title']}")
            parts.append("")

        if extracted.get("description"):
            parts.append(f"*{extracted['description']}*")
            parts.append("")

        if extracted.get("url"):
            parts.append(f"Source: {extracted['url']}")
            parts.append("")

        if extracted.get("content"):
            parts.append(extracted["content"])

        return "\n".join(parts)

    def get_metadata(self, url: str) -> Dict[str, Any]:
        """Get metadata about the page."""
        html_content = self._fetch_html(url)
        if not html_content:
            return {}

        try:
            soup = BeautifulSoup(html_content, "html.parser")

            metadata = {"url": url}

            if soup.title and soup.title.string:
                metadata["title"] = soup.title.string.strip()

            meta_desc = soup.find("meta", attrs={"name": "description"})
            if meta_desc and meta_desc.get("content"):
                metadata["description"] = str(meta_desc["content"])

            author = soup.find("meta", attrs={"name": "author"})
            if author and author.get("content"):
                metadata["author"] = str(author["content"])

            for prop in ["article:published_time", "datePublished"]:
                date_tag = soup.find("meta", property=prop)
                if date_tag and date_tag.get("content"):
                    metadata["published_date"] = str(date_tag["content"])
                    break

            return metadata

        except Exception:
            logger.exception(f"Error extracting metadata from {url}")
            return {"url": url}
