from __future__ import annotations

from typing import Any, Dict, Literal, Optional, Tuple

from lxml import html as lxml_html


class HTMLParser:
    """Utility helpers for extracting data from HTML responses."""

    @staticmethod
    def parse_field(
        html_content: str,
        selector: str,
        *,
        selector_type: Literal["css", "xpath"] = "xpath",
        attribute: Optional[str] = None,
        extract: Literal["text", "attribute", "html"] = "text",
    ) -> Tuple[Optional[Any], Optional[str]]:
        try:
            tree = lxml_html.fromstring(html_content)
            elements = (
                tree.cssselect(selector)
                if selector_type == "css"
                else tree.xpath(selector)
            )

            if not elements:
                return None, None

            if selector_type == "xpath" and isinstance(elements[0], str):
                return (elements[0] if len(elements) == 1 else elements), None

            if extract == "text":
                values = [el.text_content().strip() for el in elements]
            elif extract == "attribute":
                if not attribute:
                    return None, "attribute parameter required when extract='attribute'"
                values = [el.get(attribute) for el in elements]
            else:  # html
                values = [lxml_html.tostring(el, encoding="unicode") for el in elements]

            return (values[0] if len(values) == 1 else values), None

        except Exception as exc:  # pragma: no cover - parsing failure details
            return None, str(exc)

    @staticmethod
    def parse_multiple(
        html_content: str,
        fields: Dict[str, Dict[str, Any]],
    ) -> Tuple[Dict[str, Any], Dict[str, str]]:
        parsed: Dict[str, Any] = {}
        errors: Dict[str, str] = {}

        for field_name, config in fields.items():
            value, error = HTMLParser.parse_field(
                html_content,
                config["selector"],
                selector_type=config.get("selector_type", "css"),
                attribute=config.get("attribute"),
                extract=config.get("extract", "text"),
            )
            if error:
                errors[f"PARSE_ERROR_{field_name.upper()}"] = error
            parsed[field_name] = value

        return parsed, errors

