#!/usr/bin/env python3
"""
BASER v1.1 03/03/26 JSP (i Claude AI)

Baser - Script per superposar fotografies sobre una imatge base (fons paspartú).

Ús:
    python baser.py <imatge_base> <carpeta_origen> <carpeta_destí> [percentatge]

Arguments:
    imatge_base     : Ruta a la imatge que s'usarà com a fons/base
    carpeta_origen  : Carpeta amb les fotografies a processar
    carpeta_destí   : Carpeta on es guardaran les imatges resultants
    percentatge     : % de la imatge respecte la base (per defecte: 100)

Exemples:
    python baser.py base.jpg fotos/ sortida/
    python baser.py base.png fotos/ sortida/ 80
"""

import sys
import os
import argparse
from pathlib import Path
from PIL import Image
import piexif


# Extensions d'imatge suportades
EXTENSIONS_SUPORTADES = {".jpg", ".jpeg", ".png", ".tiff", ".tif", ".webp", ".bmp"}


def carregar_exif(ruta_imatge: Path) -> bytes | None:
    """Intenta carregar les dades EXIF d'una imatge. Retorna None si no en té."""
    try:
        exif_dict = piexif.load(str(ruta_imatge))
        return piexif.dump(exif_dict)
    except Exception:
        return None


def corregir_orientacio(img: Image.Image) -> Image.Image:
    """
    Corregeix l'orientació de la imatge segons la tag EXIF Orientation.
    Retorna la imatge rotada/reflectida correctament.
    """
    try:
        exif_data = img._getexif()
        if exif_data is None:
            return img

        # Tag 274 = Orientation
        orientacio = exif_data.get(274)
        if orientacio is None:
            return img

        transformacions = {
            2: Image.FLIP_LEFT_RIGHT,
            3: Image.ROTATE_180,
            4: Image.FLIP_TOP_BOTTOM,
            5: Image.TRANSPOSE,
            6: Image.ROTATE_270,
            7: Image.TRANSVERSE,
            8: Image.ROTATE_90,
        }

        if orientacio in transformacions:
            img = img.transpose(transformacions[orientacio])

    except Exception:
        pass  # Si no podem llegir EXIF, retornem la imatge sense modificar

    return img


def processar_imatge(
    ruta_original: Path,
    imatge_base: Image.Image,
    carpeta_desti: Path,
    percentatge: float,
) -> bool:
    """
    Processa una imatge: la redimensiona i la centra sobre la imatge base.

    Returns:
        True si s'ha processat correctament, False si hi ha hagut un error.
    """
    try:
        # Carregar la imatge original
        with Image.open(ruta_original) as img_original:
            # Corregir orientació segons tag EXIF abans de qualsevol processament
            img_corregida = corregir_orientacio(img_original)

            # Convertir a RGBA per gestionar transparències durant la composició
            if img_corregida.mode != "RGBA":
                img_processada = img_corregida.convert("RGBA")
            else:
                img_processada = img_corregida.copy()

            # Dimensions de la base
            ample_base, alt_base = imatge_base.size

            # Dimensions màximes per a la imatge original (segons el percentatge)
            ample_max = int(ample_base * percentatge / 100)
            alt_max = int(alt_base * percentatge / 100)

            # Redimensionar mantenint proporcions (fit dins del quadre màxim)
            img_processada.thumbnail((ample_max, alt_max), Image.LANCZOS)

            # Crear còpia de la base per no modificar l'original
            resultat = imatge_base.copy().convert("RGBA")

            # Calcular posició centrada
            ample_foto, alt_foto = img_processada.size
            pos_x = (ample_base - ample_foto) // 2
            pos_y = (alt_base - alt_foto) // 2

            # Superposar la foto sobre la base
            resultat.paste(img_processada, (pos_x, pos_y), img_processada)

            # Determinar format de sortida i nom de fitxer
            extensio = ruta_original.suffix.lower()
            nom_sortida = f"B{ruta_original.name}"
            ruta_sortida = carpeta_desti / nom_sortida

            # Convertir al mode adequat segons el format de sortida
            if extensio in {".jpg", ".jpeg"}:
                # JPEG no suporta transparència
                resultat_final = resultat.convert("RGB")
                format_sortida = "JPEG"
                opcions_guardат = {"quality": 95, "subsampling": 0}
            elif extensio == ".png":
                resultat_final = resultat
                format_sortida = "PNG"
                opcions_guardат = {"optimize": True}
            elif extensio in {".tiff", ".tif"}:
                resultat_final = resultat.convert("RGB")
                format_sortida = "TIFF"
                opcions_guardат = {}
            elif extensio == ".webp":
                resultat_final = resultat
                format_sortida = "WEBP"
                opcions_guardат = {"quality": 95}
            else:
                resultat_final = resultat.convert("RGB")
                format_sortida = None  # PIL detecta automàticament
                opcions_guardат = {}

            # Guardar la imatge resultant
            if format_sortida:
                resultat_final.save(str(ruta_sortida), format=format_sortida, **opcions_guardат)
            else:
                resultat_final.save(str(ruta_sortida), **opcions_guardат)

        # Transferir metadades EXIF (només per a JPEG/TIFF)
        if extensio in {".jpg", ".jpeg", ".tiff", ".tif"}:
            dades_exif = carregar_exif(ruta_original)
            if dades_exif:
                try:
                    # Reseteja l'orientació a 1 (normal/apaisat) perquè la rotació
                    # ja s'ha aplicat píxel a píxel i cap visor la torni a girar
                    exif_dict = piexif.load(dades_exif)
                    exif_dict["0th"][piexif.ImageIFD.Orientation] = 1
                    dades_exif_corregides = piexif.dump(exif_dict)
                    piexif.insert(dades_exif_corregides, str(ruta_sortida))
                except Exception as e:
                    print(f"  ⚠  No s'han pogut transferir les dades EXIF: {e}")

        print(f"  ✓  {ruta_original.name}  →  {nom_sortida}")
        return True

    except Exception as e:
        print(f"  ✗  Error processant '{ruta_original.name}': {e}")
        return False


def main():
    # --- Argument parser ---
    parser = argparse.ArgumentParser(
        description="Afegeix una imatge base (passepartout) a totes les fotos d'una carpeta.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__,
    )
    parser.add_argument("imatge_base", help="Ruta a la imatge base (fons)")
    parser.add_argument("carpeta_origen", help="Carpeta amb les fotografies originals")
    parser.add_argument("carpeta_desti", help="Carpeta de destí per a les imatges processades")
    parser.add_argument(
        "percentatge",
        nargs="?",
        type=float,
        default=100.0,
        help="Percentatge de la foto respecte la base (per defecte: 100)",
    )

    args = parser.parse_args()

    # --- Validacions ---
    ruta_base = Path(args.imatge_base)
    if not ruta_base.is_file():
        print(f"❌ Error: La imatge base '{ruta_base}' no existeix.")
        sys.exit(1)

    carpeta_origen = Path(args.carpeta_origen)
    if not carpeta_origen.is_dir():
        print(f"❌ Error: La carpeta d'origen '{carpeta_origen}' no existeix.")
        sys.exit(1)

    carpeta_desti = Path(args.carpeta_desti)
    carpeta_desti.mkdir(parents=True, exist_ok=True)

    percentatge = args.percentatge
    if not (1 <= percentatge <= 100):
        print(f"❌ Error: El percentatge ha d'estar entre 1 i 100 (valor rebut: {percentatge}).")
        sys.exit(1)

    # --- Carregar imatge base ---
    try:
        imatge_base = Image.open(ruta_base).convert("RGBA")
        print(f"\n🖼  Imatge base: '{ruta_base.name}'  ({imatge_base.width}×{imatge_base.height} px)")
    except Exception as e:
        print(f"❌ Error carregant la imatge base: {e}")
        sys.exit(1)

    # --- Cercar imatges a processar ---
    imatges = sorted(
        [
            f
            for f in carpeta_origen.iterdir()
            if f.is_file() and f.suffix.lower() in EXTENSIONS_SUPORTADES
        ]
    )

    if not imatges:
        print(f"⚠  No s'han trobat imatges a '{carpeta_origen}'.")
        sys.exit(0)

    print(f"📁  Origen:      '{carpeta_origen}'")
    print(f"📁  Destí:       '{carpeta_desti}'")
    print(f"📐  Percentatge: {percentatge}%")
    print(f"🔍  Imatges trobades: {len(imatges)}\n")
    print("─" * 55)

    # --- Processar imatges ---
    correctes = 0
    errors = 0

    for ruta_imatge in imatges:
        ok = processar_imatge(ruta_imatge, imatge_base, carpeta_desti, percentatge)
        if ok:
            correctes += 1
        else:
            errors += 1

    # --- Resum ---
    print("─" * 55)
    print(f"\n✅  Processades correctament: {correctes}")
    if errors:
        print(f"❌  Errors:                   {errors}")
    print(f"\nImatges guardades a: '{carpeta_desti}'\n")


if __name__ == "__main__":
    main()
