"""
XML parser — generic lxml-based structural extraction.

Walks the element tree depth-first:
- Root element -> structural:title
- Child elements with text -> text:narrative
- Preserves tag names as part of the text for context
"""

from __future__ import annotations

from collections.abc import Iterator

from .base import BaseParser, FileRecord, RawElement


def _element_text(el) -> str:  # type: ignore[no-untyped-def]
    """Extract direct text content from an element (not children)."""
    parts = []
    if el.text and el.text.strip():
        parts.append(el.text.strip())
    for child in el:
        if child.tail and child.tail.strip():
            parts.append(child.tail.strip())
    return " ".join(parts)


def _tag_local(el) -> str:
    """Get local tag name without namespace."""
    tag = el.tag
    if isinstance(tag, str):
        return tag.split("}")[-1] if "}" in tag else tag
    return str(tag)


def _walk_xml(el, depth: int = 0) -> Iterator[RawElement]:
    """Recursively walk XML tree and yield RawElement records."""
    tag = _tag_local(el)

    # Heading-like tags
    HEADING_TAGS = {"title", "heading", "head", "h1", "h2", "h3", "h4", "chapter", "section"}
    # Skip structural/metadata tags
    SKIP_TAGS = {"?xml", "!", "processing-instruction"}

    if tag in SKIP_TAGS:
        return

    text = _element_text(el)

    if depth == 0:
        # Root element as document title
        if text:
            yield RawElement(
                element_type="meta:document_title",
                text=f"{tag}: {text}" if text else tag,
                extra={"extraction_method": "xml:lxml", "confidence": 0.80},
            )
    elif tag.lower() in HEADING_TAGS and depth <= 3:
        if text:
            yield RawElement(
                element_type="structural:section_header",
                text=text,
                heading_level=min(depth, 4),
                detection_tier=3,
                extra={"extraction_method": "xml:lxml", "confidence": 0.70},
            )
    elif text:
        yield RawElement(
            element_type="text:narrative",
            text=f"[{tag}] {text}",
            extra={"extraction_method": "xml:lxml", "confidence": 0.75},
        )

    for child in el:
        yield from _walk_xml(child, depth + 1)


class XMLParser(BaseParser):
    handles = [
        ("text/xml", None),
        ("application/xml", None),
    ]

    def parse(self, file_record: FileRecord) -> Iterator[RawElement]:
        try:
            from lxml import etree
        except ImportError as e:
            raise ImportError("lxml required: pip install lxml") from e

        try:
            parser = etree.XMLParser(resolve_entities=False, no_network=True)
            tree = etree.parse(str(file_record.path), parser)
            root = tree.getroot()
        except etree.XMLSyntaxError as e:
            yield RawElement(
                element_type="meta:extraction_error",
                text=f"XML parse error: {e}",
                extra={"extraction_method": "xml:lxml", "confidence": 0.0},
            )
            return

        yield from _walk_xml(root, depth=0)
