"""Convierte HTML a markdown usando readabilipy + markdownify, con fallback a stdlib."""

import re
from html.parser import HTMLParser
from typing import Optional


# ---------------------------------------------------------------------------
# Stdlib fallback parser (no external deps)
# ---------------------------------------------------------------------------

_BLOCK_TAGS = {
    "p", "div", "article", "section", "main", "header", "footer", "aside",
    "nav", "figure", "figcaption", "blockquote", "pre", "ul", "ol", "li",
    "table", "thead", "tbody", "tr", "th", "td", "h1", "h2", "h3",
    "h4", "h5", "h6", "br", "hr",
}

_SKIP_TAGS = {
    "script", "style", "noscript", "iframe", "svg", "canvas",
    "nav", "footer", "header", "aside",
}

_HEADING_TAGS = {"h1": 1, "h2": 2, "h3": 3, "h4": 4, "h5": 5, "h6": 6}


class _HTMLToMarkdownParser(HTMLParser):
    """Minimal HTML → Markdown parser using only stdlib."""

    def __init__(self) -> None:
        super().__init__(convert_charrefs=True)
        self._parts: list[str] = []
        self._skip_depth = 0
        self._in_pre = False
        self._tag_stack: list[str] = []
        self._list_stack: list[str] = []

    def handle_starttag(self, tag: str, attrs: list) -> None:
        tag = tag.lower()
        self._tag_stack.append(tag)

        if self._skip_depth > 0:
            if tag in _SKIP_TAGS:
                self._skip_depth += 1
            return

        if tag in _SKIP_TAGS:
            self._skip_depth += 1
            return

        attrs_dict = dict(attrs)

        if tag in _HEADING_TAGS:
            level = _HEADING_TAGS[tag]
            self._parts.append(f"\n\n{'#' * level} ")

        elif tag == "p":
            self._parts.append("\n\n")

        elif tag == "br":
            self._parts.append("  \n")

        elif tag == "hr":
            self._parts.append("\n\n---\n\n")

        elif tag == "pre":
            self._in_pre = True
            self._parts.append("\n\n```\n")

        elif tag == "code" and not self._in_pre:
            self._parts.append("`")

        elif tag in ("strong", "b"):
            self._parts.append("**")

        elif tag in ("em", "i"):
            self._parts.append("*")

        elif tag == "a":
            href = attrs_dict.get("href", "")
            self._parts.append("[")
            self._parts.append(f"_href:{href}_")

        elif tag == "img":
            # Handle lazy-loaded images: prefer data-src over src
            src = attrs_dict.get("data-src") or attrs_dict.get("src", "")
            alt = attrs_dict.get("alt", "")
            self._parts.append(f"\n\n![{alt}]({src})\n\n")

        elif tag == "ul":
            self._list_stack.append("ul")
            self._parts.append("\n")

        elif tag == "ol":
            self._list_stack.append("ol")
            self._parts.append("\n")

        elif tag == "li":
            prefix = "-" if (not self._list_stack or self._list_stack[-1] == "ul") else "1."
            self._parts.append(f"\n{prefix} ")

        elif tag in ("blockquote",):
            self._parts.append("\n\n> ")

        elif tag in ("th", "td"):
            self._parts.append("| ")

        elif tag == "tr":
            self._parts.append("\n")

    def handle_endtag(self, tag: str) -> None:
        tag = tag.lower()
        if self._tag_stack and self._tag_stack[-1] == tag:
            self._tag_stack.pop()

        if self._skip_depth > 0:
            if tag in _SKIP_TAGS:
                self._skip_depth -= 1
            return

        if tag in _HEADING_TAGS:
            self._parts.append("\n\n")

        elif tag == "p":
            self._parts.append("\n\n")

        elif tag == "pre":
            self._in_pre = False
            self._parts.append("\n```\n\n")

        elif tag == "code" and not self._in_pre:
            self._parts.append("`")

        elif tag in ("strong", "b"):
            self._parts.append("**")

        elif tag in ("em", "i"):
            self._parts.append("*")

        elif tag == "a":
            # Find the matching _href: placeholder and rebuild [text](href)
            text_parts: list[str] = []
            href = ""
            while self._parts:
                part = self._parts.pop()
                if part.startswith("_href:") and part.endswith("_"):
                    href = part[6:-1]
                    # collected text_parts in reverse, also the "[" opener
                    if self._parts and self._parts[-1] == "[":
                        self._parts.pop()
                    break
                text_parts.insert(0, part)
            link_text = "".join(text_parts).strip()
            self._parts.append(f"[{link_text}]({href})")

        elif tag in ("ul", "ol"):
            if self._list_stack:
                self._list_stack.pop()
            self._parts.append("\n")

    def handle_data(self, data: str) -> None:
        if self._skip_depth > 0:
            return
        if self._in_pre:
            self._parts.append(data)
        else:
            self._parts.append(data)

    def get_markdown(self) -> str:
        raw = "".join(self._parts)
        # Collapse 3+ consecutive newlines to 2
        raw = re.sub(r"\n{3,}", "\n\n", raw)
        return raw.strip()


def _stdlib_html_to_markdown(html: str) -> str:
    """Convert HTML to markdown using only Python stdlib."""
    parser = _HTMLToMarkdownParser()
    parser.feed(html)
    return parser.get_markdown()


# ---------------------------------------------------------------------------
# Public function
# ---------------------------------------------------------------------------


def html_to_markdown(html: str) -> str:
    """Convierte HTML a markdown.

    Usa readabilipy para extraer el contenido principal (filtra nav, ads,
    boilerplate) y markdownify para convertir a markdown. Si alguna de esas
    librerias no esta disponible, usa un parser stdlib como fallback.

    Pasos:
    1. Preprocesar HTML: manejar contenido oculto (WeChat js_content),
       lazy loading images (data-src → src).
    2. Extraer contenido principal con readabilipy (basado en Mozilla
       Readability). Fallback: usar el HTML completo.
    3. Convertir a markdown con markdownify (headings ATX, strip
       script/style). Fallback: parser stdlib.

    Args:
        html: HTML completo de la pagina.

    Returns:
        Contenido de la pagina en formato markdown.
    """
    # Step 1: preprocess — handle WeChat hidden content and lazy-loaded images
    html = _preprocess_html(html)

    # Step 2: extract main content with readabilipy (optional dep)
    main_html = _extract_main_content(html)

    # Step 3: convert to markdown
    return _convert_to_markdown(main_html)


def _preprocess_html(html: str) -> str:
    """Preprocesar HTML antes de extraer contenido.

    - Expande contenido oculto de WeChat (js_content).
    - Reemplaza data-src por src en imagenes lazy-loaded.
    """
    # WeChat js_content: replace hidden wrapper divs
    html = re.sub(
        r'<div[^>]*id=["\']js_content["\'][^>]*style=["\'][^"\']*display\s*:\s*none[^"\']*["\'][^>]*>',
        '<div id="js_content">',
        html,
        flags=re.IGNORECASE,
    )

    # Lazy loading: copy data-src to src for img tags
    def replace_lazy_src(m: re.Match) -> str:
        tag = m.group(0)
        data_src_match = re.search(r'data-src=["\']([^"\']*)["\']', tag)
        if data_src_match:
            data_src = data_src_match.group(1)
            # Replace or add src attribute
            if re.search(r'\bsrc=["\']', tag):
                tag = re.sub(r'\bsrc=["\'][^"\']*["\']', f'src="{data_src}"', tag)
            else:
                tag = tag.replace("<img", f'<img src="{data_src}"', 1)
        return tag

    html = re.sub(r"<img[^>]+>", replace_lazy_src, html, flags=re.IGNORECASE)
    return html


def _extract_main_content(html: str) -> str:
    """Extraer contenido principal usando readabilipy si esta disponible."""
    try:
        from readabilipy import simple_json_from_html_string  # type: ignore

        article = simple_json_from_html_string(html, use_readability=True)
        return article.get("content") or html
    except ImportError:
        return html


def _convert_to_markdown(html: str) -> str:
    """Convertir HTML a markdown usando markdownify si esta disponible."""
    try:
        import markdownify  # type: ignore

        return markdownify.markdownify(
            html,
            heading_style="ATX",
            strip=["script", "style"],
        )
    except ImportError:
        return _stdlib_html_to_markdown(html)
