"""Atom XML parsing for the arXiv API."""

from __future__ import annotations

import re
from xml.etree.ElementTree import Element

import defusedxml.ElementTree as DefusedET

from arxivy.api.constants import ARXIV_NS, ATOM_NS, OPENSEARCH_NS
from arxivy.api.ids import normalize_arxiv_id
from arxivy.models import Author, Paper, PaperListMeta


def parse_entry(entry: Element) -> Paper:
    """Parse a single Atom ``<entry>`` element into a ``Paper``."""

    def _text(tag: str, ns: str = ATOM_NS) -> str:
        el = entry.find(f"{{{ns}}}{tag}")
        return (el.text or "").strip() if el is not None else ""

    raw_id = _text("id")
    arxiv_id_no_version = normalize_arxiv_id(raw_id)

    authors: list[Author] = []
    for author_el in entry.findall(f"{{{ATOM_NS}}}author"):
        name_el = author_el.find(f"{{{ATOM_NS}}}name")
        affil_el = author_el.find(f"{{{ARXIV_NS}}}affiliation")
        authors.append(
            Author(
                name=(name_el.text or "").strip() if name_el is not None else "",
                affiliation=(affil_el.text or "").strip() if affil_el is not None else "",
            )
        )

    categories: list[str] = []
    for cat_el in entry.findall(f"{{{ATOM_NS}}}category"):
        term = cat_el.get("term", "")
        if term:
            categories.append(term)

    primary_cat_el = entry.find(f"{{{ARXIV_NS}}}primary_category")
    primary_category = primary_cat_el.get("term", "") if primary_cat_el is not None else ""

    pdf_url = ""
    abstract_url = ""
    for link_el in entry.findall(f"{{{ATOM_NS}}}link"):
        href = link_el.get("href", "")
        title = link_el.get("title", "")
        link_type = link_el.get("type", "")
        if title == "pdf" or link_type == "application/pdf":
            pdf_url = href
        elif link_el.get("rel") == "alternate":
            abstract_url = href

    title = re.sub(r"\s+", " ", _text("title")).strip()
    summary = re.sub(r"\s+", " ", _text("summary")).strip()

    return Paper(
        arxiv_id=arxiv_id_no_version,
        title=title,
        summary=summary,
        authors=authors,
        published=_text("published"),
        updated=_text("updated"),
        categories=categories,
        primary_category=primary_category,
        comment=_text("comment", ARXIV_NS),
        journal_ref=_text("journal_ref", ARXIV_NS),
        doi=_text("doi", ARXIV_NS),
        pdf_url=pdf_url,
        abstract_url=abstract_url,
    )


def parse_feed(xml_text: str) -> tuple[list[Paper], PaperListMeta]:
    """Parse an Atom feed response into (entries, meta)."""
    root = DefusedET.fromstring(xml_text)

    total_el = root.find(f"{{{OPENSEARCH_NS}}}totalResults")
    start_el = root.find(f"{{{OPENSEARCH_NS}}}startIndex")
    per_page_el = root.find(f"{{{OPENSEARCH_NS}}}itemsPerPage")

    meta = PaperListMeta(
        total_results=int(total_el.text) if total_el is not None and total_el.text else 0,
        start_index=int(start_el.text) if start_el is not None and start_el.text else 0,
        items_per_page=int(per_page_el.text) if per_page_el is not None and per_page_el.text else 0,
    )

    entries: list[Paper] = []
    for entry_el in root.findall(f"{{{ATOM_NS}}}entry"):
        parsed = parse_entry(entry_el)
        if parsed.title:
            entries.append(parsed)

    return entries, meta
