"""CORE API integration for accessing 200M+ academic papers.

CORE (COnnecting REpositories) aggregates open access research
papers from thousands of repositories worldwide.
API Documentation: https://api.core.ac.uk/docs
"""
from typing import List, Optional, Dict
from datetime import datetime
import requests
import os
from ..paper import Paper
from ..pdf_utils import extract_text_from_pdf
import logging

logger = logging.getLogger(__name__)


class CoreSearcher:
    """Searcher for CORE academic paper repository.

    CORE provides access to over 200M open access papers from
    repositories worldwide including arXiv, PubMed Central, and more.
    """

    BASE_URL = "https://api.core.ac.uk/v3"

    def __init__(self, api_key: Optional[str] = None):
        """Initialize CORE searcher.

        Args:
            api_key: Optional CORE API key for higher limits
                    Get one at https://core.ac.uk/api-keys
        """
        self.api_key = api_key or os.environ.get("CORE_API_KEY")
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'paper-search-mcp/1.0',
            'Accept': 'application/json'
        })
        if self.api_key:
            self.session.headers['Authorization'] = f'Bearer {self.api_key}'

    def search(
        self,
        query: str,
        max_results: int = 10,
        date_from: Optional[str] = None,
        date_to: Optional[str] = None,
        repository_id: Optional[int] = None,
        **kwargs
    ) -> List[Paper]:
        """Search for papers in CORE.

        Args:
            query: Search query string
            max_results: Maximum number of papers to return (default: 10, max: 100)
            date_from: Start date YYYY-MM-DD (e.g., '2024-01-01')
            date_to: End date YYYY-MM-DD (e.g., '2024-12-31')
            repository_id: Optional CORE repository ID to filter by
            **kwargs: Additional parameters (offset, sort, etc.)

        Returns:
            List of Paper objects
        """
        papers = []

        try:
            url = f"{self.BASE_URL}/search/works"

            params = {
                "q": query,
                "limit": min(max_results, 100),
                "offset": kwargs.get("offset", 0)
            }

            # Add date filters
            if date_from:
                params["yearFrom"] = date_from[:4]
            if date_to:
                params["yearTo"] = date_to[:4]

            # Filter by repository
            if repository_id:
                params["repositoryId"] = repository_id

            # Add sorting
            if "sort" in kwargs:
                params["sort"] = kwargs["sort"]

            response = self.session.get(url, params=params, timeout=30)
            response.raise_for_status()
            data = response.json()

            results = data.get("results", [])
            total_count = data.get("totalHits", 0)

            logger.info(f"CORE search: found {total_count} results for '{query}'")

            for item in results:
                try:
                    paper = self._parse_result(item)
                    if paper:
                        papers.append(paper)
                except Exception as e:
                    logger.warning(f"Error parsing CORE result: {e}")
                    continue

        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 429:
                logger.warning("CORE rate limit exceeded")
            else:
                logger.error(f"CORE API error: {e}")
        except Exception as e:
            logger.error(f"CORE search error: {e}")

        return papers

    def _parse_result(self, item: Dict) -> Optional[Paper]:
        """Parse a CORE search result into a Paper object.

        Args:
            item: CORE API result item

        Returns:
            Paper object or None
        """
        try:
            paper_id = str(item.get("id", ""))
            title = item.get("title", "") or ""

            # Authors
            authors = []
            for author in item.get("authors", []):
                name = author.get("name", "") if isinstance(author, dict) else str(author)
                if name:
                    authors.append(name)

            abstract = item.get("abstract", "") or ""

            # DOI
            doi = item.get("doi", "") or ""

            # Publication date
            published_date = None
            year = item.get("yearPublished")
            if year:
                try:
                    published_date = datetime(int(year), 1, 1)
                except:
                    pass

            # URLs
            download_url = item.get("downloadUrl", "") or ""
            source_url = item.get("sourceFulltextUrls", [])
            if source_url and isinstance(source_url, list):
                source_url = source_url[0]
            else:
                source_url = ""

            pdf_url = download_url if download_url.endswith(".pdf") else ""

            # Repository info
            data_provider = item.get("dataProvider", {})
            source_name = data_provider.get("name", "CORE") if isinstance(data_provider, dict) else "CORE"

            # Subjects/categories
            categories = []
            for subject in item.get("subjects", []):
                if subject:
                    categories.append(subject)

            return Paper(
                paper_id=paper_id,
                title=title,
                authors=authors,
                abstract=abstract[:5000] if abstract else "",
                doi=doi,
                published_date=published_date or datetime.min,
                pdf_url=pdf_url or download_url,
                url=source_url or f"https://core.ac.uk/works/{paper_id}",
                source="core",
                categories=categories[:10],
                citations=item.get("citationCount", 0) or 0,
                extra={
                    "core_id": paper_id,
                    "source_repository": source_name,
                    "download_url": download_url,
                    "language": item.get("language", {}).get("code", "") if isinstance(item.get("language"), dict) else "",
                    "year": year
                }
            )

        except Exception as e:
            logger.error(f"Error parsing CORE result: {e}")
            return None

    def get_paper_by_id(self, paper_id: str) -> Optional[Paper]:
        """Get a specific paper by its CORE ID.

        Args:
            paper_id: CORE paper ID

        Returns:
            Paper object or None
        """
        try:
            url = f"{self.BASE_URL}/works/{paper_id}"
            response = self.session.get(url, timeout=30)
            response.raise_for_status()
            data = response.json()
            return self._parse_result(data)
        except Exception as e:
            logger.error(f"Error fetching CORE paper {paper_id}: {e}")
            return None

    def search_by_doi(self, doi: str) -> Optional[Paper]:
        """Search for a paper by its DOI.

        Args:
            doi: Digital Object Identifier

        Returns:
            Paper object or None
        """
        doi = doi.replace("https://doi.org/", "").replace("doi:", "").strip()
        results = self.search(f'doi:"{doi}"', max_results=1)
        return results[0] if results else None

    def download_pdf(self, paper_id: str, save_path: str = "./downloads") -> str:
        """Download PDF from CORE.

        Args:
            paper_id: CORE paper ID
            save_path: Directory to save

        Returns:
            Path to downloaded PDF or error message
        """
        paper = self.get_paper_by_id(paper_id)
        if not paper:
            return f"Paper {paper_id} not found"

        pdf_url = paper.pdf_url or paper.extra.get("download_url", "")
        if not pdf_url:
            return f"No PDF available for paper {paper_id}"

        try:
            os.makedirs(save_path, exist_ok=True)

            response = self.session.get(pdf_url, timeout=60)
            response.raise_for_status()

            content_type = response.headers.get("Content-Type", "")
            if "pdf" in content_type or pdf_url.endswith(".pdf"):
                filename = f"core_{paper_id}.pdf"
                file_path = os.path.join(save_path, filename)

                with open(file_path, 'wb') as f:
                    f.write(response.content)

                return file_path
            else:
                return f"URL does not point to a PDF: {pdf_url}"

        except Exception as e:
            logger.error(f"Error downloading CORE PDF: {e}")
            return f"Failed to download PDF: {e}"

    def read_paper(self, paper_id: str, save_path: str = "./downloads") -> str:
        """Read and extract text from a CORE paper PDF.

        Args:
            paper_id: CORE paper ID
            save_path: Directory for PDF storage

        Returns:
            Extracted text or error message
        """
        pdf_path = self.download_pdf(paper_id, save_path)
        if not os.path.exists(pdf_path):
            return pdf_path  # Return error message

        text = extract_text_from_pdf(pdf_path)
        return text if text else "Failed to extract text from PDF"
