from __future__ import annotations

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

from src.errors import UserError
from src.images.paths import gbp_dest_paths
from src.matching.io import iter_image_files, validate_inbox_dir


@dataclass(frozen=True)
class GbpExportSummary:
    inbox_dir: Path
    out_dir: Path
    dry_run: bool
    files_found: int
    files_exported: int

    def to_human_summary(self) -> str:
        mode = "DRY-RUN" if self.dry_run else "RUN"
        return (
            f"{mode} gbp export summary\n"
            f"- inbox: {self.inbox_dir}\n"
            f"- out: {self.out_dir}\n"
            f"- files_found: {self.files_found}\n"
            f"- files_exported: {self.files_exported}"
        )


def export_gbp(*, inbox_dir: Path, out_dir: Path, min_size: int, dry_run: bool) -> GbpExportSummary:
    validate_inbox_dir(inbox_dir)
    if int(min_size) <= 0:
        raise UserError("Invalid min_size.")

    files = iter_image_files(inbox_dir)
    exported = 0
    for src in files:
        dest_jpg, dest_png = gbp_dest_paths(out_dir=out_dir, inbox_dir=inbox_dir, src=src)
        if dry_run:
            continue
        _export_single(src=src, dest_jpg=dest_jpg, dest_png=dest_png, min_size=int(min_size))
        exported += 1

    return GbpExportSummary(
        inbox_dir=inbox_dir,
        out_dir=out_dir,
        dry_run=dry_run,
        files_found=len(files),
        files_exported=exported,
    )


def _export_single(*, src: Path, dest_jpg: Path, dest_png: Path, min_size: int) -> None:
    img = _open_image(src)
    img2 = _ensure_min_size(img, min_size=min_size)
    dest_jpg.parent.mkdir(parents=True, exist_ok=True)
    dest_png.parent.mkdir(parents=True, exist_ok=True)
    _save_jpeg(img2, dest_jpg)
    _save_png(img2, dest_png)


def _require_pillow() -> Any:
    try:
        from PIL import Image
    except ModuleNotFoundError as exc:
        raise UserError("Missing dependency: Pillow (install via pip).") from exc
    return Image


def _open_image(path: Path) -> Any:
    Image = _require_pillow()
    try:
        with Image.open(path) as img:
            return img.copy()
    except Exception as exc:
        raise UserError(f"Failed to open image: {path} ({exc})") from exc


def _ensure_min_size(img: Any, *, min_size: int) -> Any:
    Image = _require_pillow()
    width, height = getattr(img, "size", (0, 0))
    if width >= min_size and height >= min_size:
        return img
    scale = max(min_size / max(1, width), min_size / max(1, height))
    new_w = int(round(width * scale))
    new_h = int(round(height * scale))
    return img.resize((new_w, new_h), resample=Image.LANCZOS)


def _save_jpeg(img: Any, path: Path) -> None:
    try:
        rgb = img.convert("RGB") if getattr(img, "mode", "") != "RGB" else img
        rgb.save(path, format="JPEG", quality=92)
    except Exception as exc:
        raise UserError(f"Failed to write JPG: {path} ({exc})") from exc


def _save_png(img: Any, path: Path) -> None:
    try:
        img.save(path, format="PNG")
    except Exception as exc:
        raise UserError(f"Failed to write PNG: {path} ({exc})") from exc
