"""Heading-hierarchy check for Markdown / Quarto / HTML files."""

import re
from pathlib import Path


def check_headings(path, min_chars: int = 3) -> list[dict]:
    """Scan a file for three heading defects:

    (a) skipped levels (e.g., ``##`` followed by ``####``),
    (b) empty headings,
    (c) headings shorter than ``min_chars`` (likely non-descriptive).

    These violate WCAG 2.1 Success Criterion 1.3.1 (Info and Relationships), 2.4.6
    (Headings and Labels), and 2.4.10 (Section Headings). Supported
    extensions: ``.md``, ``.qmd``, ``.Rmd``, ``.html``.

    Returns a list of dicts with keys ``line``, ``level``, ``text``,
    ``issue``. Empty list if the document has a clean hierarchy.
    """
    path = Path(path)
    if not path.exists():
        raise FileNotFoundError(f"File not found: {path}")

    headings = (
        _parse_headings_html(path)
        if path.suffix.lower() == ".html"
        else _parse_headings_md(path)
    )
    if not headings:
        return []

    issues = _skip_rows(headings) + _empty_rows(headings) + _short_rows(headings, min_chars)
    return sorted(issues, key=lambda r: r["line"])


def _skip_rows(headings):
    return [
        {**h, "issue": f"Level skip from h{headings[i-1]['level']} to h{h['level']}"}
        for i, h in enumerate(headings)
        if i > 0 and h["level"] - headings[i - 1]["level"] > 1
    ]


def _empty_rows(headings):
    return [
        {**h, "issue": "Empty heading"}
        for h in headings if not h["text"].strip()
    ]


def _short_rows(headings, min_chars):
    return [
        {**h, "issue": f"Heading text under {min_chars} chars (likely non-descriptive)"}
        for h in headings
        if h["text"].strip() and len(h["text"].strip()) < min_chars
    ]


def _parse_headings_md(path):
    lines = path.read_text(encoding="utf-8").splitlines()
    yaml_end = _yaml_frontmatter_end(lines)
    in_fence = False
    out = []
    for i, line in enumerate(lines, start=1):
        if i <= yaml_end:
            continue
        if line.startswith("```"):
            in_fence = not in_fence
            continue
        if in_fence:
            continue
        m = re.match(r"^(#{1,6})(?:\s+(.*))?$", line)
        if m:
            out.append({"line": i, "level": len(m.group(1)), "text": m.group(2) or ""})
    return out


def _yaml_frontmatter_end(lines):
    if len(lines) < 2 or lines[0] != "---":
        return 0
    for j, line in enumerate(lines[1:], start=2):
        if line == "---":
            return j
    return 0


def _parse_headings_html(path):
    raw = path.read_text(encoding="utf-8")
    pattern = re.compile(r"<h([1-6])[^>]*>(.*?)</h[1-6]>", re.IGNORECASE | re.DOTALL)
    return [
        {
            "line":  i,
            "level": int(m.group(1)),
            "text":  re.sub(r"<[^>]+>", "", m.group(2)).strip(),
        }
        for i, m in enumerate(pattern.finditer(raw), start=1)
    ]
