from __future__ import annotations

import re
from dataclasses import dataclass
from hashlib import sha1
from pathlib import Path

from pdftomd.models import DocumentIdentity


_ANCHOR_PARTS = re.compile(r"[a-z0-9]+")


def normalize_pdf_path(path: str | Path, *, must_exist: bool = True) -> Path:
    """Normalize a local PDF path without invoking any parser."""

    normalized = Path(path).expanduser()
    if normalized.suffix.lower() != ".pdf":
        raise ValueError(f"Input path must point to a PDF file: {path}")

    if must_exist:
        if not normalized.exists():
            raise FileNotFoundError(f"PDF file does not exist: {path}")
        if not normalized.is_file():
            raise ValueError(f"PDF path is not a file: {path}")
        return normalized.resolve()

    return normalized.resolve(strict=False)


def document_identity_from_pdf(path: str | Path, *, title: str | None = None) -> DocumentIdentity:
    normalized = normalize_pdf_path(path)
    return DocumentIdentity.from_path(str(normalized), title=title)


def make_anchor(kind: str, value: str) -> str:
    kind_slug = _anchor_fragment(kind)
    value_slug = _anchor_fragment(value)
    if not kind_slug:
        raise ValueError("anchor kind must contain at least one supported character")
    if not value_slug:
        raise ValueError("anchor value must contain at least one supported character")
    return f"{kind_slug}-{value_slug}"


@dataclass(frozen=True)
class OutputBundlePaths:
    output_root: Path
    document: DocumentIdentity
    document_dir: Path
    images_dir: Path
    runtime_dir: Path
    log_path: Path
    resume_state_path: Path

    @classmethod
    def from_document(cls, output_root: str | Path, document: DocumentIdentity) -> "OutputBundlePaths":
        root = Path(output_root)
        document_dir = root / document.slug
        runtime_dir = root / ".pdftomd-runtime" / document.slug
        return cls(
            output_root=root,
            document=document,
            document_dir=document_dir,
            images_dir=document_dir / document.asset_dir,
            runtime_dir=runtime_dir,
            log_path=runtime_dir / "logs" / "conversion.log",
            resume_state_path=runtime_dir / "state" / "resume-state.json",
        )

    def chunk_path(self, index: int) -> Path:
        return self.document_dir / self.document.chunk_filename(index)

    def figure_asset_path(self, figure_number: str) -> Path:
        return self.images_dir / self.document.figure_asset_filename(figure_number)

    def anchor(self, kind: str, value: str) -> str:
        return make_anchor(kind, value)


def _anchor_fragment(value: str) -> str:
    parts = _ANCHOR_PARTS.findall(value.lower())
    if parts:
        return "-".join(parts)
    if not value:
        return ""
    return f"document-{sha1(value.encode('utf-8')).hexdigest()[:10]}"
