from pathlib import Path
from urllib.parse import quote
import hashlib
import html
import shutil
import subprocess

from PIL import Image, ImageOps

ROOT = Path(__file__).resolve().parent
OUTPUT = ROOT / "index.html"
THUMBS = ROOT / "thumbs"

MEDIA_EXTENSIONS = {
    ".mp4", ".mkv", ".avi", ".mov", ".wmv", ".m4v", ".webm",
    ".jpg", ".jpeg", ".png", ".gif", ".webp",
}
IGNORED_FILES = {"index.html", "generate_index.py", ".DS_Store", "Thumbs.db"}
THUMB_SIZE = (480, 270)


def relative_url(path):
    """Return a URL-encoded path relative to the folder containing index.html."""
    return quote(path.relative_to(ROOT).as_posix(), safe="/")


def thumbnail_name(path):
    # Include the relative path in the hash so duplicate filenames don't collide.
    digest = hashlib.sha1(path.relative_to(ROOT).as_posix().encode()).hexdigest()[:12]
    return f"{path.stem}-{digest}.jpg"


def make_image_thumbnail(source, destination):
    with Image.open(source) as image:
        image = ImageOps.exif_transpose(image).convert("RGB")
        image.thumbnail(THUMB_SIZE, Image.Resampling.LANCZOS)

        canvas = Image.new("RGB", THUMB_SIZE, "black")
        x = (THUMB_SIZE[0] - image.width) // 2
        y = (THUMB_SIZE[1] - image.height) // 2
        canvas.paste(image, (x, y))
        canvas.save(destination, "JPEG", quality=85)


def make_video_thumbnail(source, destination):
    if not shutil.which("ffmpeg"):
        return False

    result = subprocess.run(
        [
            "ffmpeg", "-y", "-ss", "00:00:01", "-i", str(source),
            "-frames:v", "1", "-vf",
            f"scale={THUMB_SIZE[0]}:{THUMB_SIZE[1]}:force_original_aspect_ratio=decrease,"
            f"pad={THUMB_SIZE[0]}:{THUMB_SIZE[1]}:(ow-iw)/2:(oh-ih)/2:black",
            str(destination),
        ],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        check=False,
    )
    return result.returncode == 0 and destination.exists()


def main():
    THUMBS.mkdir(exist_ok=True)

    files = [
        path for path in ROOT.rglob("*")
        if path.is_file()
        and path.suffix.lower() in MEDIA_EXTENSIONS
        and path.name not in IGNORED_FILES
        and THUMBS not in path.parents
    ]
    files.sort(key=lambda path: path.stat().st_mtime, reverse=True)

    cards = []
    for path in files:
        thumb_path = THUMBS / thumbnail_name(path)

        try:
            if path.suffix.lower() in {".jpg", ".jpeg", ".png", ".gif", ".webp"}:
                make_image_thumbnail(path, thumb_path)
            else:
                make_video_thumbnail(path, thumb_path)
        except Exception as error:
            print(f"Could not create thumbnail for {path}: {error}")

        file_url = relative_url(path)
        label = html.escape(path.relative_to(ROOT).as_posix())

        if thumb_path.exists():
            preview = (
                f'<img src="{relative_url(thumb_path)}" '
                f'alt="{label}" loading="lazy">'
            )
        else:
            preview = '<div class="placeholder">Preview unavailable</div>'

        cards.append(
            f'<a class="card" href="{file_url}">'
            f'{preview}<span class="filename">{label}</span></a>'
        )

    page = """<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Media index</title>
<style>
  * { box-sizing: border-box; }
  body {
    margin: 0;
    min-height: 100vh;
    color: #fff;
    font: 16px/1.45 system-ui, sans-serif;
    background: linear-gradient(to bottom, #505050, #333);
  }
  main { width: min(1400px, 100%); margin: auto; padding: 24px; }
  h1 { margin: 0 0 20px; font-size: 1.6rem; }
  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 16px;
  }
  .card {
    display: block;
    overflow: hidden;
    color: inherit;
    text-decoration: none;
    background: rgba(0, 0, 0, .22);
    border: 2px solid #fff;
    border-radius: 2px;
  }
  .card img, .placeholder {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: contain;
    background: #111;
  }
  .placeholder {
    display: grid;
    place-items: center;
    padding: 12px;
    color: #ccc;
    text-align: center;
  }
  .filename {
    display: block;
    padding: 10px;
    overflow-wrap: anywhere;
  }
  .card:hover { background: rgba(0, 0, 0, .4); }
</style>
</head>
<body>
<main>
<h1>Media index</h1>
<div class="grid">
""" + "\n".join(cards) + """
</div>
</main>
</body>
</html>
"""

    OUTPUT.write_text(page, encoding="utf-8")
    print(f"Created {OUTPUT} with {len(files)} media file(s).")


if __name__ == "__main__":
    main()
