"""
arXiv PDF and Text Downloader
"""

import re
from typing import Dict, Optional
from urllib.parse import urlparse
from loguru import logger

from .base import BaseDownloader, ContentType, DownloadResult, USER_AGENT


class ArxivDownloader(BaseDownloader):
    """Downloader for arXiv papers with PDF and abstract/text support."""

    def can_handle(self, url: str) -> bool:
        """Check if URL is from arXiv."""
        try:
            hostname = urlparse(url).hostname
            return bool(
                hostname
                and (hostname == "arxiv.org" or hostname.endswith(".arxiv.org"))
            )
        except Exception:
            return False

    def download(
        self, url: str, content_type: ContentType = ContentType.PDF
    ) -> Optional[bytes]:
        """Download content from arXiv."""
        if content_type == ContentType.TEXT:
            return self._download_text(url)
        return self._download_pdf(url)

    def download_with_result(
        self, url: str, content_type: ContentType = ContentType.PDF
    ) -> DownloadResult:
        """Download content and return detailed result with skip reason."""
        # Extract arXiv ID
        arxiv_id = self._extract_arxiv_id(url)
        if not arxiv_id:
            return DownloadResult(
                skip_reason="Invalid arXiv URL - could not extract article ID"
            )

        if content_type == ContentType.TEXT:
            # ArXiv API only provides abstracts, not full text
            # We need to download the PDF and extract full text
            logger.info(
                f"Downloading arXiv PDF for full text extraction: {arxiv_id}"
            )

            pdf_content = self._download_pdf(url)
            if pdf_content:
                extracted_text = self.extract_text_from_pdf(pdf_content)
                if extracted_text:
                    # Optionally prepend metadata from API
                    metadata = self._fetch_from_arxiv_api(arxiv_id)
                    if metadata:
                        # Combine metadata with full text
                        full_text = f"{metadata}\n\n{'=' * 80}\nFULL PAPER TEXT\n{'=' * 80}\n\n{extracted_text}"
                        return DownloadResult(
                            content=full_text.encode("utf-8", errors="ignore"),
                            is_success=True,
                        )
                    # Just return the extracted text
                    return DownloadResult(
                        content=extracted_text.encode("utf-8", errors="ignore"),
                        is_success=True,
                    )

            return DownloadResult(
                skip_reason=f"Could not retrieve full text for arXiv:{arxiv_id}"
            )
        # Download PDF
        pdf_url = f"https://arxiv.org/pdf/{arxiv_id}.pdf"
        logger.info(f"Downloading arXiv PDF: {arxiv_id}")

        pdf_content = super()._download_pdf(pdf_url)
        if pdf_content:
            return DownloadResult(content=pdf_content, is_success=True)
        return DownloadResult(
            skip_reason=f"Failed to download PDF for arXiv:{arxiv_id} - server may be unavailable"
        )

    def _download_pdf(
        self, url: str, headers: Optional[Dict[str, str]] = None
    ) -> Optional[bytes]:
        """Download PDF from arXiv."""
        # Extract arXiv ID
        arxiv_id = self._extract_arxiv_id(url)
        if not arxiv_id:
            logger.error(f"Could not extract arXiv ID from {url}")
            return None

        # Construct PDF URL
        pdf_url = f"https://arxiv.org/pdf/{arxiv_id}.pdf"

        logger.info(f"Downloading arXiv PDF: {arxiv_id}")

        # Use honest user agent - arXiv supports academic tools with proper identification
        enhanced_headers = {
            "User-Agent": USER_AGENT,
            "Accept": "application/pdf,application/octet-stream,*/*",
            "Accept-Language": "en-US,en;q=0.9",
            "Accept-Encoding": "gzip, deflate, br",
            "Connection": "keep-alive",
        }

        return super()._download_pdf(pdf_url, headers=enhanced_headers)

    def _download_text(self, url: str) -> Optional[bytes]:
        """Get full text content from arXiv PDF (with metadata from API)."""
        # Extract arXiv ID
        arxiv_id = self._extract_arxiv_id(url)
        if not arxiv_id:
            return None

        # Download PDF for full text extraction
        logger.info(f"Downloading arXiv PDF for full text: {arxiv_id}")
        pdf_content = self._download_pdf(url)
        if pdf_content:
            extracted_text = self.extract_text_from_pdf(pdf_content)
            if extracted_text:
                # Get metadata from API to prepend
                metadata = self._fetch_from_arxiv_api(arxiv_id)
                if metadata:
                    # Combine metadata with full text
                    full_text = f"{metadata}\n\n{'=' * 80}\nFULL PAPER TEXT\n{'=' * 80}\n\n{extracted_text}"
                    return full_text.encode("utf-8", errors="ignore")
                return extracted_text.encode("utf-8", errors="ignore")

        return None

    def _extract_arxiv_id(self, url: str) -> Optional[str]:
        """Extract arXiv ID from URL."""
        # Handle different arXiv URL formats
        patterns = [
            r"arxiv\.org/abs/(\d+\.\d+)(?:v\d+)?",  # New format: 2301.12345 or 2301.12345v2
            r"arxiv\.org/pdf/(\d+\.\d+)(?:v\d+)?",  # PDF URL with optional version
            r"arxiv\.org/abs/([a-z-]+/\d+)(?:v\d+)?",  # Old format: cond-mat/0501234
            r"arxiv\.org/pdf/([a-z-]+/\d+)(?:v\d+)?",  # Old PDF URL with optional version
        ]

        for pattern in patterns:
            match = re.search(pattern, url)
            if match:
                return match.group(1)

        return None

    def _fetch_from_arxiv_api(self, arxiv_id: str) -> Optional[str]:
        """Fetch abstract and metadata from arXiv API."""
        try:
            # Clean the ID for API query
            clean_id = arxiv_id.replace("/", "")

            # Query arXiv API
            api_url = f"https://export.arxiv.org/api/query?id_list={clean_id}"
            response = self.session.get(api_url, timeout=10)

            if response.status_code == 200:
                # Parse the Atom feed response
                # Use defusedxml to prevent XXE attacks
                from defusedxml import ElementTree as ET

                root = ET.fromstring(response.text)

                # Define namespaces (URIs are identifiers, not URLs to fetch)
                ns = {
                    "atom": "http://www.w3.org/2005/Atom",  # DevSkim: ignore DS137138
                    "arxiv": "http://arxiv.org/schemas/atom",  # DevSkim: ignore DS137138
                }

                # Find the entry
                entry = root.find("atom:entry", ns)
                if entry is not None:
                    # Extract text content
                    text_parts = []

                    # Title
                    title = entry.find("atom:title", ns)
                    if title is not None and title.text:
                        text_parts.append(f"Title: {title.text.strip()}")

                    # Authors
                    authors = entry.findall("atom:author", ns)
                    if authors:
                        author_names = []
                        for author in authors:
                            name = author.find("atom:name", ns)
                            if name is not None and name.text:
                                author_names.append(name.text.strip())
                        if author_names:
                            text_parts.append(
                                f"Authors: {', '.join(author_names)}"
                            )

                    # Abstract
                    summary = entry.find("atom:summary", ns)
                    if summary is not None and summary.text:
                        text_parts.append(
                            f"\nAbstract:\n{summary.text.strip()}"
                        )

                    # Categories
                    categories = entry.findall("atom:category", ns)
                    if categories:
                        cat_terms = [
                            cat.get("term")
                            for cat in categories
                            if cat.get("term")
                        ]
                        if cat_terms:
                            text_parts.append(
                                f"\nCategories: {', '.join(cat_terms)}"
                            )

                    if text_parts:
                        logger.info(
                            f"Retrieved text content from arXiv API for {arxiv_id}"
                        )
                        return "\n".join(text_parts)

        except Exception as e:
            logger.debug(f"Failed to fetch from arXiv API: {e}")

        return None
