"""Detect headings in a PDF by analyzing font size distribution."""

from collections import Counter

import pdfplumber


def detect_headings_by_font(
    pdf: pdfplumber.PDF,
    min_delta: float = 2.0,
    max_levels: int = 4,
) -> list[dict]:
    """Detect headings by analyzing font size distribution across pages.

    The most common font size is treated as body text. Font sizes significantly
    larger than body (by at least min_delta) and appearing in fewer than 50% of
    chars are classified as heading levels.

    Args:
        pdf: An open pdfplumber.PDF object.
        min_delta: Minimum size difference above body size to qualify as heading.
        max_levels: Maximum number of heading levels to detect.

    Returns:
        list[dict]: List of {"level": int, "title": str, "page_num": int}
                    sorted by page number. Returns empty list if no headings detected.
    """
    if not pdf.pages:
        return []

    # Step 1: Sample font sizes from every 5th page to determine body size
    size_counter: Counter = Counter()
    sample_pages = [pdf.pages[i] for i in range(0, len(pdf.pages), 5)]
    if not sample_pages:
        sample_pages = [pdf.pages[0]]

    for page in sample_pages:
        try:
            chars = page.chars
            for ch in chars:
                size = ch.get("size")
                if size is not None:
                    size_counter[round(float(size), 1)] += 1
        except Exception:
            continue

    if not size_counter:
        return []

    # Step 2: Determine body size (most common font size)
    body_size, body_count = size_counter.most_common(1)[0]

    # Step 3: Identify heading sizes
    # Must be >= body_size + min_delta and frequency < 50% of body count
    heading_sizes = sorted(
        [
            size
            for size, count in size_counter.items()
            if size >= body_size + min_delta and count < body_count * 0.5
        ],
        reverse=True,
    )[:max_levels]

    if not heading_sizes:
        return []

    # Build size -> level mapping
    size_to_level = {size: i + 1 for i, size in enumerate(heading_sizes)}

    # Step 4: Collect heading text per page
    raw_headings: list[dict] = []
    total_pages = len(pdf.pages)

    for page_idx, page in enumerate(pdf.pages):
        page_num = page_idx + 1
        try:
            chars = page.chars
        except Exception:
            continue

        # Group consecutive chars of same heading size into text blocks
        current_size = None
        current_text = []

        for ch in chars:
            size = ch.get("size")
            if size is None:
                continue
            rounded = round(float(size), 1)
            if rounded in size_to_level:
                if rounded == current_size:
                    current_text.append(ch.get("text", ""))
                else:
                    if current_text and current_size is not None:
                        text = "".join(current_text).strip()
                        if text:
                            raw_headings.append({
                                "level": size_to_level[current_size],
                                "title": text,
                                "page_num": page_num,
                            })
                    current_size = rounded
                    current_text = [ch.get("text", "")]
            else:
                if current_text and current_size is not None:
                    text = "".join(current_text).strip()
                    if text:
                        raw_headings.append({
                            "level": size_to_level[current_size],
                            "title": text,
                            "page_num": page_num,
                        })
                current_size = None
                current_text = []

        # Flush remaining
        if current_text and current_size is not None:
            text = "".join(current_text).strip()
            if text:
                raw_headings.append({
                    "level": size_to_level[current_size],
                    "title": text,
                    "page_num": page_num,
                })

    if not raw_headings:
        return []

    # Step 5: Deduplicate — remove titles appearing on > 30% of pages (headers/footers)
    title_page_counts: Counter = Counter(h["title"] for h in raw_headings)
    threshold = total_pages * 0.3

    filtered = [h for h in raw_headings if title_page_counts[h["title"]] <= threshold]

    return filtered
