"""Optional GROBID-based PDF parser for structured scientific document extraction.

GROBID (GeneRation Of BIbliographic Data) is a machine learning tool for
extracting structured content from scientific PDFs. It produces TEI XML with
labeled sections, authors, references, etc.

Requirements:
    - A running GROBID server (local or remote).
    - pip install grobid-client-python

Usage:
    If GROBID is available, it produces more reliable section extraction than
    regex-based parsing, especially for two-column layouts and complex formatting.

    The function `extract_with_grobid()` returns the same format as the regex
    parser so they are interchangeable.

See: https://grobid.readthedocs.io/
"""

from __future__ import annotations

import logging
import re
import xml.etree.ElementTree as ET
from pathlib import Path
from typing import Optional

logger = logging.getLogger("res_sum")

# TEI XML namespace used by GROBID
TEI_NS = {"tei": "http://www.tei-c.org/ns/1.0"}


def is_grobid_available(grobid_url: str = "http://localhost:8070") -> bool:
    """Check if a GROBID server is reachable."""
    try:
        import requests
        resp = requests.get(f"{grobid_url}/api/isalive", timeout=5)
        return resp.status_code == 200
    except Exception:
        return False


def extract_with_grobid(
    pdf_path: str | Path,
    grobid_url: str = "http://localhost:8070",
) -> tuple[str, dict[str, str]]:
    """Extract text and sections from a PDF using GROBID.

    Args:
        pdf_path: Path to the PDF file.
        grobid_url: URL of the GROBID server.

    Returns:
        Tuple of (full_text, sections_dict) — same format as
        parsers.pdf_parser.extract_sections().

    Raises:
        RuntimeError: If GROBID is not available or parsing fails.
    """
    try:
        import requests
    except ImportError:
        raise RuntimeError(
            "requests package required for GROBID. Install with: pip install requests"
        )

    pdf_path = Path(pdf_path)
    if not pdf_path.exists():
        raise FileNotFoundError(f"PDF not found: {pdf_path}")

    # Call GROBID's processFulltextDocument endpoint
    with open(pdf_path, "rb") as f:
        resp = requests.post(
            f"{grobid_url}/api/processFulltextDocument",
            files={"input": (pdf_path.name, f, "application/pdf")},
            timeout=120,
        )

    if resp.status_code != 200:
        raise RuntimeError(
            f"GROBID returned status {resp.status_code}: {resp.text[:200]}"
        )

    # Parse TEI XML
    return _parse_tei_xml(resp.text)


def _parse_tei_xml(xml_text: str) -> tuple[str, dict[str, str]]:
    """Parse GROBID TEI XML output into sections."""
    root = ET.fromstring(xml_text)

    sections: dict[str, str] = {
        "introduction": "",
        "methodology": "",
        "results": "",
        "discussion": "",
        "conclusion": "",
    }

    # Map GROBID head text to our canonical section names
    section_mapping = {
        "introduction": "introduction",
        "background": "introduction",
        "methods": "methodology",
        "methodology": "methodology",
        "materials and methods": "methodology",
        "materials & methods": "methodology",
        "study area": "methodology",
        "study site": "methodology",
        "field methods": "methodology",
        "data collection": "methodology",
        "statistical analysis": "methodology",
        "experimental design": "methodology",
        "results": "results",
        "results and discussion": "results",
        "discussion": "discussion",
        "conclusion": "conclusion",
        "conclusions": "conclusion",
        "concluding remarks": "conclusion",
    }

    # Find all <div> elements in the body
    body = root.find(".//tei:body", TEI_NS)
    if body is None:
        # Fallback: extract all text
        all_text = ET.tostring(root, encoding="unicode", method="text")
        return all_text, sections

    all_text_parts = []
    current_section = None

    for div in body.findall(".//tei:div", TEI_NS):
        # Get the heading
        head = div.find("tei:head", TEI_NS)
        head_text = (head.text or "").strip().lower() if head is not None else ""

        # Map to canonical section
        for key, canonical in section_mapping.items():
            if key in head_text:
                current_section = canonical
                break

        # Extract all paragraph text from this div
        paragraphs = []
        for p in div.findall(".//tei:p", TEI_NS):
            p_text = "".join(p.itertext()).strip()
            if p_text:
                paragraphs.append(p_text)

        div_text = "\n".join(paragraphs)
        all_text_parts.append(div_text)

        if current_section and div_text:
            sections[current_section] += div_text + "\n"

    full_text = "\n\n".join(all_text_parts)
    return full_text, sections
