"""
Pheser — Remaker
Replaces EXIF metadata with synthetic or user-supplied values.
"""

from __future__ import annotations
import os
from datetime import datetime
from typing import Any

from PIL import Image
import piexif

from .utils import output_path, deg_to_rational


_DEFAULT_META = {
    "make": "Generic",
    "model": "Camera",
    "software": "Pheser",
    "artist": "",
    "copyright": "",
    "description": "",
    "datetime": None,          # defaults to now
}


def remake(
    input_file: str,
    output_file: str | None = None,
    *,
    make: str = "Generic",
    model: str = "Camera",
    software: str = "Pheser",
    artist: str = "",
    copyright: str = "",
    description: str = "",
    datetime_str: str | None = None,
    inject_gps: tuple[float, float] | None = None,
    out_dir: str | None = None,
    verbose: bool = True,
) -> str | None:
    """
    Recreate EXIF metadata on an image with custom or synthetic values.

    Parameters
    ----------
    input_file  : Source image path.
    output_file : Destination path.
    make        : Camera manufacturer string.
    model       : Camera model string.
    software    : Software string.
    artist      : Artist / author string.
    copyright   : Copyright notice string.
    description : Image description string.
    datetime_str: DateTime string "YYYY:MM:DD HH:MM:SS". Defaults to now.
    inject_gps  : Optional (latitude, longitude) tuple to embed GPS data.
    out_dir     : Output directory.
    verbose     : Print progress.

    Returns
    -------
    Output file path on success, None on failure.
    """
    if not os.path.isfile(input_file):
        print(f"[pheser]  File not found: {input_file}")
        return None

    if output_file is None:
        output_file = output_path(input_file, "remake", out_dir)

    try:
        img = Image.open(input_file)
    except Exception as e:
        print(f"[pheser]  Cannot open image: {e}")
        return None

    dt = datetime_str or datetime.now().strftime("%Y:%m:%d %H:%M:%S")

    # Tags in 0th IFD that affect display geometry -- must be carried over as-is.
    _PRESERVE_0TH = {
        piexif.ImageIFD.Orientation,
        piexif.ImageIFD.XResolution,
        piexif.ImageIFD.YResolution,
        piexif.ImageIFD.ResolutionUnit,
        piexif.ImageIFD.YCbCrPositioning,
    }

    original_0th = {}
    original_1st = {}
    original_thumbnail = None
    if "exif" in img.info:
        try:
            original = piexif.load(img.info["exif"])
            original_0th = {k: v for k, v in original.get("0th", {}).items()
                            if k in _PRESERVE_0TH}
            original_1st = original.get("1st", {})
            original_thumbnail = original.get("thumbnail")
        except Exception:
            pass

    exif_dict: dict[str, Any] = {
        "0th": original_0th,
        "Exif": {},
        "GPS": {},
        "1st": original_1st,
        "thumbnail": original_thumbnail,
    }

    def _b(s: str) -> bytes:
        return s.encode("utf-8") if s else b""

    exif_dict["0th"][piexif.ImageIFD.Make] = _b(make)
    exif_dict["0th"][piexif.ImageIFD.Model] = _b(model)
    exif_dict["0th"][piexif.ImageIFD.Software] = _b(software)
    exif_dict["0th"][piexif.ImageIFD.DateTime] = _b(dt)

    if artist:
        exif_dict["0th"][piexif.ImageIFD.Artist] = _b(artist)
    if copyright:
        exif_dict["0th"][piexif.ImageIFD.Copyright] = _b(copyright)
    if description:
        exif_dict["0th"][piexif.ImageIFD.ImageDescription] = _b(description)

    exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = _b(dt)
    exif_dict["Exif"][piexif.ExifIFD.DateTimeDigitized] = _b(dt)

    if inject_gps:
        lat, lon = inject_gps
        lat_ref = b"N" if lat >= 0 else b"S"
        lon_ref = b"E" if lon >= 0 else b"W"
        exif_dict["GPS"][piexif.GPSIFD.GPSLatitudeRef] = lat_ref
        exif_dict["GPS"][piexif.GPSIFD.GPSLatitude] = deg_to_rational(lat)
        exif_dict["GPS"][piexif.GPSIFD.GPSLongitudeRef] = lon_ref
        exif_dict["GPS"][piexif.GPSIFD.GPSLongitude] = deg_to_rational(lon)

    exif_bytes = piexif.dump(exif_dict)
    img.save(output_file, exif=exif_bytes)

    if verbose:
        print(f"[pheser]  Metadata recreated")
        print(f"[pheser]    Make/Model : {make} / {model}")
        print(f"[pheser]    DateTime   : {dt}")
        if inject_gps:
            print(f"[pheser]    GPS        : {inject_gps[0]}, {inject_gps[1]}")
        print(f"[pheser]    Saved → {output_file}")

    return output_file