from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any

import fitz

from pdftomd.models import PageAnalysis, PageRange


DEFAULT_CHUNK_SIZE = 20
MIN_TEXT_LAYER_LENGTH = 20
OCR_TEXT_LENGTH_THRESHOLD = 200


@dataclass(frozen=True)
class DocumentPreAnalysis:
    page_count: int
    pages: tuple[PageAnalysis, ...]
    chunk_candidates: tuple[PageRange, ...]

    @property
    def ocr_pages(self) -> tuple[int, ...]:
        return tuple(page.page for page in self.pages if page.needs_ocr)


def is_ocr_candidate(
    text_length: int,
    image_count: int,
    *,
    min_text_layer_length: int = MIN_TEXT_LAYER_LENGTH,
    ocr_text_length_threshold: int = OCR_TEXT_LENGTH_THRESHOLD,
) -> bool:
    """Deterministic text-layer heuristic for later Marker OCR handoff."""

    if text_length < 0:
        raise ValueError("text_length must be >= 0")
    if image_count < 0:
        raise ValueError("image_count must be >= 0")
    if text_length < min_text_layer_length:
        return True
    return image_count > 0 and text_length < ocr_text_length_threshold


def plan_page_chunks(
    page_count: int,
    *,
    target_pages: int = DEFAULT_CHUNK_SIZE,
) -> tuple[PageRange, ...]:
    if page_count < 0:
        raise ValueError("page_count must be >= 0")
    if target_pages < 1:
        raise ValueError("target_pages must be >= 1")
    if page_count == 0:
        return ()

    ranges: list[PageRange] = []
    for start in range(1, page_count + 1, target_pages):
        end = min(start + target_pages - 1, page_count)
        ranges.append(PageRange(start=start, end=end))
    return tuple(ranges)


def analyze_pdf(
    pdf_path: str | Path,
    *,
    chunk_target_pages: int = DEFAULT_CHUNK_SIZE,
) -> DocumentPreAnalysis:
    path = Path(pdf_path)
    pages: list[PageAnalysis] = []

    with fitz.open(str(path)) as document:
        page_count = document.page_count
        for index, page in enumerate(document, start=1):
            pages.append(_analyze_page(index, page))

    return DocumentPreAnalysis(
        page_count=page_count,
        pages=tuple(pages),
        chunk_candidates=plan_page_chunks(
            page_count,
            target_pages=chunk_target_pages,
        ),
    )


def _analyze_page(page_number: int, page: Any) -> PageAnalysis:
    text = page.get_text("text") or ""
    text_length = len(text.strip())
    image_count = len(page.get_images(full=True))
    has_text_layer = text_length >= MIN_TEXT_LAYER_LENGTH

    return PageAnalysis(
        page=page_number,
        text_length=text_length,
        image_count=image_count,
        has_text_layer=has_text_layer,
        needs_ocr=is_ocr_candidate(text_length, image_count),
    )
