#!/usr/bin/env python3

import argparse
import os
from datetime import datetime
from pathlib import Path

from PIL import Image
from PIL.ExifTags import TAGS


IMAGE_EXTENSIONS = {
    ".jpg",
    ".jpeg",
    ".jpe",
    ".jfif",
}


def get_exif_timestamp(image_path):
    """
    Extract the original capture timestamp from JPEG EXIF data.

    Preference:
        DateTimeOriginal
        DateTimeDigitized
        DateTime
    """

    try:
        with Image.open(image_path) as image:

            exif = image.getexif()

            if not exif:
                return None

            # Map numeric EXIF IDs to human-readable names.
            exif_data = {
                TAGS.get(tag_id, tag_id): value
                for tag_id, value in exif.items()
            }

            for tag in (
                "DateTimeOriginal",
                "DateTimeDigitized",
                "DateTime",
            ):
                value = exif_data.get(tag)

                if not value:
                    continue

                if not isinstance(value, str):
                    continue

                # Standard EXIF timestamp:
                # YYYY:MM:DD HH:MM:SS
                try:
                    return datetime.strptime(
                        value.strip(),
                        "%Y:%m:%d %H:%M:%S"
                    )

                except ValueError:
                    pass

    except Exception as exc:
        print(f"[ERROR] {image_path}: {exc}")

    return None


def make_unique_path(path):
    """
    Avoid overwriting an existing file.

    Example:

        2026-09-20_14-32-01_photo.jpg

    becomes:

        2026-09-20_14-32-01_photo_1.jpg
        2026-09-20_14-32-01_photo_2.jpg
    """

    if not path.exists():
        return path

    counter = 1

    while True:

        candidate = (
            path.parent
            / f"{path.stem}_{counter}{path.suffix}"
        )

        if not candidate.exists():
            return candidate

        counter += 1


def process_directory(directory, recursive=False, dry_run=False):
    """
    Enumerate JPEG files and rename those with EXIF timestamps.
    """

    directory = Path(directory)

    if not directory.is_dir():
        raise ValueError(
            f"Not a directory: {directory}"
        )

    if recursive:
        files = directory.rglob("*")
    else:
        files = directory.iterdir()

    jpeg_count = 0
    renamed_count = 0
    no_exif_count = 0

    for path in files:

        if not path.is_file():
            continue

        if path.suffix.lower() not in IMAGE_EXTENSIONS:
            continue

        jpeg_count += 1

        timestamp = get_exif_timestamp(path)

        if timestamp is None:
            print(
                f"[NO EXIF TIMESTAMP] {path}"
            )
            no_exif_count += 1
            continue

        # Format:
        # yyyy-MM-dd_HH-mm-ss_
        timestamp_string = timestamp.strftime(
            "%Y-%m-%d_%H-%M-%S"
        )

        new_name = (
            f"{timestamp_string}_{path.name}"
        )

        new_path = path.parent / new_name

        # Already correctly named.
        if path == new_path:
            print(
                f"[ALREADY NAMED] {path}"
            )
            continue

        # Never overwrite an existing file.
        new_path = make_unique_path(new_path)

        print(
            f"[RENAME]\n"
            f"    OLD: {path}\n"
            f"    NEW: {new_path}"
        )

        if not dry_run:
            path.rename(new_path)

        renamed_count += 1

    print()
    print("=" * 70)
    print(f"JPEG files found : {jpeg_count}")
    print(f"Files renamed    : {renamed_count}")
    print(f"No EXIF time     : {no_exif_count}")
    print(f"Dry run          : {dry_run}")
    print("=" * 70)


def main():

    parser = argparse.ArgumentParser(
        description=(
            "Rename JPEG files using their EXIF capture timestamp."
        )
    )

    parser.add_argument(
        "directory",
        help="Directory containing image files"
    )

    parser.add_argument(
        "-r",
        "--recursive",
        action="store_true",
        help="Recursively process subdirectories"
    )

    parser.add_argument(
        "--dry-run",
        action="store_true",
        help=(
            "Show what would be renamed without "
            "actually changing files"
        )
    )

    args = parser.parse_args()

    process_directory(
        args.directory,
        recursive=args.recursive,
        dry_run=args.dry_run,
    )


if __name__ == "__main__":
    main()