"""
Plain text parser — heuristic heading detection, line-based processing.

Heading detection heuristics (Tier 3):
- Short ALL-CAPS lines (≤ 60 chars)
- Lines ending with ':' that are short (≤ 50 chars)
- Numbered section patterns: "1.", "1.1", "1.1.1 Title"
"""

from __future__ import annotations

import re
from collections.abc import Iterator

from .base import BaseParser, FileRecord, RawElement

_NUMBERED_HEADING = re.compile(r"^(\d+(?:\.\d+)*)\s+([A-Z][^\n]{2,60})$")
_ALLCAPS_HEADING = re.compile(r"^[A-Z][A-Z\s\-:]{2,58}[A-Z]$")
_COLON_HEADING = re.compile(r"^[A-Z][^\n]{3,48}:$")


def _detect_heading_level(text: str) -> int | None:
    """Return heading level 1-3 if line looks like a heading, else None."""
    stripped = text.strip()
    if not stripped or len(stripped) > 70:
        return None

    m = _NUMBERED_HEADING.match(stripped)
    if m:
        depth = len(m.group(1).split("."))
        return min(depth, 3)

    if _ALLCAPS_HEADING.match(stripped):
        return 1

    if _COLON_HEADING.match(stripped):
        return 2

    return None


class PlaintextParser(BaseParser):
    handles = [("text/plain", None)]

    def parse(self, file_record: FileRecord) -> Iterator[RawElement]:
        try:
            from charset_normalizer import from_path

            result = from_path(str(file_record.path)).best()
            if result is None:
                text = file_record.path.read_text(encoding="utf-8", errors="replace")
            else:
                text = str(result)
        except Exception:
            text = file_record.path.read_text(encoding="utf-8", errors="replace")

        paragraph_lines: list[str] = []

        def flush_paragraph() -> Iterator[RawElement]:
            if paragraph_lines:
                para = " ".join(paragraph_lines).strip()
                if para:
                    yield RawElement(
                        element_type="text:narrative",
                        text=para,
                        extra={"extraction_method": "plaintext:heuristic", "confidence": 0.80},
                    )
                paragraph_lines.clear()

        for line in text.splitlines():
            stripped = line.strip()

            if not stripped:
                yield from flush_paragraph()
                continue

            level = _detect_heading_level(stripped)
            if level is not None:
                yield from flush_paragraph()
                yield RawElement(
                    element_type="structural:section_header",
                    text=stripped,
                    heading_level=level,
                    detection_tier=3,
                    extra={"extraction_method": "plaintext:heuristic", "confidence": 0.65},
                )
            else:
                paragraph_lines.append(stripped)

        yield from flush_paragraph()
