"""Plot file processing and HTML rendering."""

import shutil
import subprocess
from pathlib import Path
from typing import Any, Dict, Optional

from jinja2 import Template

from gallery.config import GalleryConfig
from gallery.utils.metadata import (
    get_metadata_file_path,
    resolve_metadata_for_plot,
)
from gallery.utils.stats import (
    calculate_directory_stats,
    format_file_size,
)

try:
    import fitz  # PyMuPDF

    _PYMUPDF_AVAILABLE = True
except ImportError:
    _PYMUPDF_AVAILABLE = False

_IMAGEMAGICK_AVAILABLE = shutil.which("convert") is not None


def process_html_file(html_file: Path, web_dir: Path, current_metadata: Optional[Dict[str, Any]] = None) -> dict:
    """
    Process HTML plot file, copying it to web directory.

    Args:
        html_file: Path to the source HTML file
        web_dir: Target web directory
        current_metadata: Current metadata dictionary for the plot

    Returns:
        Dictionary containing plot information
    """
    web_html = web_dir / html_file.name

    if needs_update(html_file, web_html):
        shutil.copy2(html_file, web_html)

    # Get source file creation time
    source_creation_time = int(html_file.stat().st_ctime)

    # Resolve metadata if provided
    plot_metadata = {}
    if current_metadata is not None:
        plot_metadata = resolve_metadata_for_plot(html_file, current_metadata)

    return {
        "name": html_file.stem,
        "html_href": html_file.name,
        "is_html": True,
        "metadata": plot_metadata,
        "creation_time": source_creation_time,
    }


def process_plot_files(
    config: GalleryConfig,
    plot_file: Path,
    web_dir: Path,
    current_metadata: Optional[Dict[str, Any]] = None,
) -> dict:
    """
    Process plot files (PDF/PNG or HTML), handling conversion and copying.

    Args:
        config: Gallery configuration object
        plot_file: Path to the source plot file (PDF or HTML)
        web_dir: Target web directory
        current_metadata: Current metadata dictionary for the plot

    Returns:
        Dictionary containing plot information
    """
    if plot_file.suffix.lower() == ".html":
        return process_html_file(plot_file, web_dir, current_metadata)

    # Handle PDF files
    png_file = plot_file.with_suffix(".png")
    web_pdf = web_dir / plot_file.name
    web_png = web_dir / png_file.name

    if needs_update(plot_file, web_pdf):
        shutil.copy2(plot_file, web_pdf)

    if not png_file.exists():
        convert_pdf_to_png(plot_file, config=config)

    if needs_update(png_file, web_png):
        shutil.copy2(png_file, web_png)

    source_creation_time = int(plot_file.stat().st_ctime)

    plot_metadata = {}
    if current_metadata is not None:
        plot_metadata = resolve_metadata_for_plot(plot_file, current_metadata)

    return {
        "name": plot_file.stem,
        "pdf_href": plot_file.name,
        "png_href": png_file.name,
        "is_html": False,
        "metadata": plot_metadata,
        "creation_time": source_creation_time,
    }


def render_gallery_page(
    config: GalleryConfig,
    template: Template,
    web_dir: Path,
    items: list,
    subdirs: list,
    relative_path: Path,
    title: Optional[str] = None,
    metadata: Optional[dict] = None,
) -> None:
    """
    Unified template rendering for all gallery pages.

    Args:
        config: Gallery configuration object
        template: Jinja2 template object
        web_dir: Target web directory
        items: List of plot items
        subdirs: List of subdirectory names
        relative_path: Relative path from gallery root
        title: Page title (optional)
        metadata: Metadata dictionary (optional)
    """
    if title is None:
        title = "Gallery" if relative_path == Path(".") else f"Gallery: {relative_path}"

    if metadata is None:
        metadata = {}

    # Calculate statistics
    current_stats = calculate_directory_stats(web_dir)
    stats = {
        "file_count": len(items),
        "folder_count": len(subdirs),
        "total_size": format_file_size(current_stats["total_size"]),
        "total_size_bytes": current_stats["total_size"],
    }

    # Calculate relative path to assets
    if relative_path == Path("."):
        assets_path = "../assets"
    else:
        depth = len(relative_path.parts)
        assets_path = "../" * (depth + 1) + "assets"

    # For root level, show only directory structure
    if relative_path == Path("."):
        items = []

    output_html = web_dir / "index.html"
    with output_html.open("w") as f:
        paths_dict = {
            "web_folder": str(config.web_folder),
        }
        ui_dict = {
            "max_recent_plots": 20,
            "search_debounce_ms": 300,
        }
        rendered_html = template.render(
            title=title,
            items=items,
            subdirs=subdirs,
            relpath=str(relative_path),
            paths=paths_dict,
            ui=ui_dict,
            stats=stats,
            folder_metadata=metadata,
            assets_path=assets_path,
            source_dir=str(web_dir),
            metadata_file_path=get_metadata_file_path(web_dir),
        )
        f.write(rendered_html)


def convert_pdf_to_png(pdf_path: Path, config: GalleryConfig) -> None:
    """
    Convert a PDF file to PNG format.

    Uses PyMuPDF (fitz) when available; falls back to ImageMagick otherwise.
    Only converts if the PNG doesn't exist or if the PDF is newer than
    the PNG (with a 30-second buffer to handle filesystem timing issues).

    Raises:
        RuntimeError: If neither PyMuPDF nor ImageMagick is available.
        subprocess.CalledProcessError: If the ImageMagick fallback fails.
    """
    png_path = pdf_path.with_suffix(".png")

    if png_path.exists():
        pdf_mtime = pdf_path.stat().st_mtime
        png_mtime = png_path.stat().st_mtime
        if png_mtime >= (pdf_mtime + 30):
            return

    if _PYMUPDF_AVAILABLE:
        _convert_pdf_pymupdf(pdf_path, png_path, config.png_dpi)
    elif _IMAGEMAGICK_AVAILABLE:
        _convert_pdf_imagemagick(pdf_path, png_path, config.png_dpi)
    else:
        raise RuntimeError(
            "No PDF renderer found. Install PyMuPDF (`pip install pymupdf`) "
            "or ImageMagick (`apt-get install imagemagick`)."
        )


def _convert_pdf_pymupdf(pdf_path: Path, png_path: Path, dpi: int) -> None:
    zoom = dpi / 72  # PDF coordinate space is 72 pt/inch
    doc = fitz.open(str(pdf_path))
    try:
        pix = doc[0].get_pixmap(matrix=fitz.Matrix(zoom, zoom))
        pix.save(str(png_path))
    finally:
        doc.close()


def _convert_pdf_imagemagick(pdf_path: Path, png_path: Path, dpi: int) -> None:
    subprocess.run(
        [
            "convert",
            "-density",
            str(dpi),
            str(pdf_path),
            "-quality",
            "95",
            str(png_path),
        ],
        check=True,
    )


def needs_update(source_file: Path, target_file: Path) -> bool:
    """
    Check if target file needs updating based on source modification time.

    Args:
        source_file: Path to the source file
        target_file: Path to the target file

    Returns:
        True if target needs update, False otherwise
    """
    if not target_file.exists():
        return True

    source_mtime = source_file.stat().st_mtime
    target_mtime = target_file.stat().st_mtime

    return source_mtime > (target_mtime + 30)
