#!/usr/bin/env python3
"""
Upload Thumbnail Generator for AEI Photo API

Generates WebP thumbnails and mid-sized images for uploaded photos.
Called by uploadlocallat_kuldeep.php after photo upload.

Usage:
    python3 upload_thumb_generator.py <source_image> <job_folder>

Arguments:
    source_image: Full path to the original uploaded image
    job_folder: Full path to the job folder (contains the source image)

Output Structure:
    {job_folder}/
    ├── photo.jpg             (source - not modified)
    └── thumbs/               (WebP thumbnails - 200x200)
        ├── photo.webp
        └── mid/              (WebP mid-sized - 800px max)
            └── photo.webp

All image generation runs in the background after the API has responded.
The remote server generates its own thumbnails independently.

Author: AEI Photo System
Version: 1.0
Date: 2026-02-04
"""

import os
import sys
import logging
from PIL import Image, ImageOps
from pathlib import Path

# Configuration
THUMBNAIL_SIZE = (200, 200)      # Gallery thumbnail size
MAX_MIDSIZED = 800               # Gallery mid-sized max dimension
WEBP_THUMB_QUALITY = 70          # WebP quality for thumbnails
WEBP_MID_QUALITY = 80            # WebP quality for mid-sized

# Supported image extensions
SUPPORTED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.heic', '.heif'}

# Setup logging
LOG_FILE = '/var/log/upload_thumb_generator.log'
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE, mode='a'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger(__name__)


def is_heic(file_path: str) -> bool:
    """Check if file is HEIC/HEIF format."""
    ext = Path(file_path).suffix.lower()
    return ext in {'.heic', '.heif'}


def load_image(file_path: str) -> Image.Image:
    """Load image and apply EXIF rotation."""
    try:
        if is_heic(file_path):
            # Try pillow-heif for HEIC support
            try:
                from pillow_heif import register_heif_opener
                register_heif_opener()
            except ImportError:
                # Fallback to ImageMagick via subprocess
                import subprocess
                import tempfile

                with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp:
                    tmp_path = tmp.name

                result = subprocess.run(
                    ['convert', file_path, tmp_path],
                    capture_output=True,
                    timeout=30
                )

                if result.returncode != 0:
                    raise Exception(f"ImageMagick conversion failed: {result.stderr.decode()}")

                img = Image.open(tmp_path)
                os.unlink(tmp_path)
                return ImageOps.exif_transpose(img)

        img = Image.open(file_path)
        return ImageOps.exif_transpose(img)

    except Exception as e:
        logger.error(f"Failed to load image {file_path}: {e}")
        raise


def create_thumbnail(img: Image.Image, size: tuple) -> Image.Image:
    """Create thumbnail maintaining aspect ratio."""
    thumb = img.copy()
    thumb.thumbnail(size, Image.LANCZOS)
    return thumb


def create_midsized(img: Image.Image, max_dimension: int) -> Image.Image:
    """Create mid-sized image maintaining aspect ratio."""
    width, height = img.size

    if width <= max_dimension and height <= max_dimension:
        return img.copy()

    if width > height:
        new_width = max_dimension
        new_height = int(height * (max_dimension / width))
    else:
        new_height = max_dimension
        new_width = int(width * (max_dimension / height))

    return img.resize((new_width, new_height), Image.LANCZOS)


def ensure_dir(directory: str) -> None:
    """Create directory if it doesn't exist."""
    Path(directory).mkdir(parents=True, exist_ok=True)


def generate_thumbnails(source_path: str, job_folder: str) -> dict:
    """
    Generate thumbnails and mid-sized images for an uploaded photo.

    Creates two variants in the background after the API has responded:
    - thumbs/: Gallery thumbnail (200x200 WebP)
    - thumbs/mid/: Gallery mid-sized (800px max WebP)

    Returns dict with paths to generated files.
    """
    results = {
        'source': source_path,
        'thumb': None,
        'mid': None,
        'errors': []
    }

    # Validate source exists
    if not os.path.isfile(source_path):
        results['errors'].append(f"Source file not found: {source_path}")
        return results

    # Get base name without extension
    source_name = Path(source_path).stem

    # Define output directories within the job folder
    thumbs_dir = os.path.join(job_folder, 'thumbs')
    mid_dir = os.path.join(thumbs_dir, 'mid')

    try:
        # Load image once
        img = load_image(source_path)

        # Ensure output directories exist
        ensure_dir(thumbs_dir)
        ensure_dir(mid_dir)

        # Generate gallery thumbnail (200x200)
        thumb_path = os.path.join(thumbs_dir, f"{source_name}.webp")
        if not os.path.exists(thumb_path):
            thumb = create_thumbnail(img, THUMBNAIL_SIZE)
            thumb.save(thumb_path, 'WEBP', quality=WEBP_THUMB_QUALITY)
            results['thumb'] = thumb_path
            logger.info(f"Created thumbnail: {thumb_path}")

        # Generate gallery mid-sized (800px max)
        mid_path = os.path.join(mid_dir, f"{source_name}.webp")
        if not os.path.exists(mid_path):
            mid = create_midsized(img, MAX_MIDSIZED)
            mid.save(mid_path, 'WEBP', quality=WEBP_MID_QUALITY)
            results['mid'] = mid_path
            logger.info(f"Created mid-sized: {mid_path}")

    except Exception as e:
        error_msg = f"Error processing {source_path}: {e}"
        results['errors'].append(error_msg)
        logger.error(error_msg)

    return results


def process_folder(job_folder: str) -> dict:
    """
    Process all images in a job folder.

    Useful for batch processing or catching up on missed thumbnails.
    """
    results = {
        'processed': 0,
        'skipped': 0,
        'errors': []
    }

    if not os.path.isdir(job_folder):
        results['errors'].append(f"Folder not found: {job_folder}")
        return results

    # Find all images in the folder (not in subdirectories)
    for filename in os.listdir(job_folder):
        file_path = os.path.join(job_folder, filename)

        # Skip directories and non-image files
        if os.path.isdir(file_path):
            continue

        ext = Path(filename).suffix.lower()
        if ext not in SUPPORTED_EXTENSIONS:
            continue

        # Generate thumbnails for this image
        result = generate_thumbnails(file_path, job_folder)

        if result['errors']:
            results['errors'].extend(result['errors'])
        elif result['thumb'] or result['mid']:
            results['processed'] += 1
        else:
            results['skipped'] += 1

    return results


def main():
    """Main entry point."""
    if len(sys.argv) < 2:
        print("Usage: python3 upload_thumb_generator.py <source_image> [job_folder]")
        print("       python3 upload_thumb_generator.py --folder <job_folder>")
        sys.exit(1)

    # Handle --folder mode for batch processing
    if sys.argv[1] == '--folder':
        if len(sys.argv) < 3:
            print("Error: --folder requires a folder path")
            sys.exit(1)

        job_folder = sys.argv[2]
        logger.info(f"Processing folder: {job_folder}")

        results = process_folder(job_folder)

        print(f"Processed: {results['processed']}")
        print(f"Skipped: {results['skipped']}")
        if results['errors']:
            print(f"Errors: {len(results['errors'])}")
            for err in results['errors']:
                print(f"  - {err}")

        sys.exit(0 if not results['errors'] else 1)

    # Single image mode
    source_image = sys.argv[1]

    # Job folder defaults to parent of source image
    if len(sys.argv) >= 3:
        job_folder = sys.argv[2]
    else:
        job_folder = os.path.dirname(source_image)

    logger.info(f"Processing image: {source_image}")
    logger.info(f"Job folder: {job_folder}")

    results = generate_thumbnails(source_image, job_folder)

    if results['errors']:
        for err in results['errors']:
            print(f"Error: {err}", file=sys.stderr)
        sys.exit(1)

    # Output paths for PHP to parse if needed
    if results['thumb']:
        print(f"thumb:{results['thumb']}")
    if results['mid']:
        print(f"mid:{results['mid']}")

    sys.exit(0)


if __name__ == "__main__":
    main()
