import logging
from typing import Optional
from urllib.parse import urljoin

import requests
from bs4 import BeautifulSoup
from django.conf import settings

logger = logging.getLogger(__name__)

SCIHUB_DOMAINS = [
    "sci-hub.ru",
    "sci-hub.ee",
    "sci-hub.st",
    "sci-hub.do",
]


class SciHubService:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update(
            {
                "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
            }
        )

    def fetch_from_url(self, url: str) -> Optional[bytes]:
        try:
            resp = self.session.get(url, timeout=60)
            if resp.status_code != 200:
                logger.warning(
                    "[pdf] URL download failed",
                    extra={"status": resp.status_code, "url": url},
                )
                return None
            if not self._looks_like_pdf(resp):
                return None
            return resp.content
        except requests.RequestException as e:
            logger.error(
                "[pdf] URL download request failed",
                extra={"url": url, "error": str(e)},
            )
            return None

    def fetch_pdf(self, doi: str) -> Optional[bytes]:
        configured_domain = getattr(settings, "SCIHUB_DOMAIN", None)
        domains_to_try = (
            [configured_domain] + SCIHUB_DOMAINS
            if configured_domain and configured_domain not in SCIHUB_DOMAINS
            else SCIHUB_DOMAINS
        )

        for domain in domains_to_try:
            url = f"https://{domain}/{doi}"
            logger.info(
                "[scihub] trying domain",
                extra={"domain": domain, "doi": doi},
            )
            try:
                response = self.session.get(url, timeout=30)
                if response.status_code != 200:
                    continue

                pdf_url = self._extract_pdf_url(response.text, url)
                if not pdf_url:
                    continue

                pdf_response = self.session.get(pdf_url, timeout=60)
                if pdf_response.status_code != 200:
                    continue

                if not self._looks_like_pdf(pdf_response):
                    continue

                logger.info(
                    "[scihub] PDF downloaded successfully",
                    extra={
                        "domain": domain,
                        "doi": doi,
                        "size": len(pdf_response.content),
                    },
                )
                return pdf_response.content
            except requests.RequestException as e:
                logger.debug(
                    "[scihub] domain failed",
                    extra={"domain": domain, "doi": doi, "error": str(e)},
                )
                continue

        logger.warning(
            "[scihub] all domains failed",
            extra={"doi": doi, "tried": domains_to_try},
        )
        return None

    def _looks_like_pdf(self, response: requests.Response) -> bool:
        content_type = response.headers.get("Content-Type", "")
        if "application/pdf" in content_type:
            return True
        if content_type.startswith("application/octet"):
            return True
        if response.url.endswith(".pdf"):
            return True
        return False

    def _extract_pdf_url(self, html: str, page_url: str) -> Optional[str]:
        soup = BeautifulSoup(html, "html.parser")

        iframe = soup.find("iframe", {"id": "pdf"})
        if iframe and iframe.get("src"):
            src = iframe["src"]
            if src.startswith("http"):
                return src
            return urljoin(page_url, src)

        embed = soup.find("embed", {"type": "application/pdf"})
        if embed and embed.get("src"):
            src = embed["src"]
            if src.startswith("http"):
                return src
            return urljoin(page_url, src)

        download_div = soup.find("div", {"id": "download"})
        if download_div:
            link = download_div.find("a")
            if link and link.get("href"):
                href = link["href"]
                if href.startswith("http"):
                    return href
                return urljoin(page_url, href)

        for link in soup.find_all("a", href=True):
            href = link["href"]
            if ".pdf" in href:
                if href.startswith("http"):
                    return href
                return urljoin(page_url, href)

        return None
