from __future__ import annotations

import os
import shutil
import tempfile
import unittest
from datetime import datetime, timezone
from pathlib import Path

import piexif

from src.exif.exif import read_exif


class TestExif(unittest.TestCase):
    def test_reads_datetime_and_gps(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            path = _copy_fixture(Path(tmp) / "with_exif.jpg")
            exif_bytes = piexif.dump(
                {
                    "0th": {},
                    "Exif": {piexif.ExifIFD.DateTimeOriginal: b"2026:01:01 12:34:56"},
                    "GPS": {
                        piexif.GPSIFD.GPSLatitudeRef: b"N",
                        piexif.GPSIFD.GPSLatitude: ((52, 1), (22, 1), (36, 1)),
                        piexif.GPSIFD.GPSLongitudeRef: b"E",
                        piexif.GPSIFD.GPSLongitude: ((4, 1), (54, 1), (12, 1)),
                    },
                    "1st": {},
                    "Interop": {},
                    "thumbnail": None,
                }
            )
            piexif.insert(exif_bytes, str(path))

            result = read_exif(path)

            self.assertEqual(result.datetime_source, "exif")
            self.assertEqual(
                result.datetime_utc,
                datetime(2026, 1, 1, 12, 34, 56, tzinfo=timezone.utc),
            )
            self.assertEqual(result.gps_source, "exif")
            self.assertIsNotNone(result.gps_lat)
            self.assertIsNotNone(result.gps_lon)
            self.assertAlmostEqual(result.gps_lat or 0.0, 52.376666, places=5)
            self.assertAlmostEqual(result.gps_lon or 0.0, 4.903333, places=5)

    def test_fallback_to_mtime_when_datetime_missing(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            path = _copy_fixture(Path(tmp) / "no_datetime.jpg")

            exif_bytes = piexif.dump(
                {
                    "0th": {},
                    "Exif": {},
                    "GPS": {
                        piexif.GPSIFD.GPSLatitudeRef: b"S",
                        piexif.GPSIFD.GPSLatitude: ((10, 1), (0, 1), (0, 1)),
                        piexif.GPSIFD.GPSLongitudeRef: b"W",
                        piexif.GPSIFD.GPSLongitude: ((20, 1), (0, 1), (0, 1)),
                    },
                    "1st": {},
                    "Interop": {},
                    "thumbnail": None,
                }
            )
            piexif.insert(exif_bytes, str(path))
            epoch = 1_700_000_000
            os.utime(path, (epoch, epoch))

            result = read_exif(path)

            self.assertEqual(result.datetime_source, "mtime")
            self.assertEqual(
                result.datetime_utc,
                datetime.fromtimestamp(epoch, tz=timezone.utc),
            )
            self.assertEqual(result.gps_source, "exif")
            self.assertAlmostEqual(result.gps_lat or 0.0, -10.0, places=5)
            self.assertAlmostEqual(result.gps_lon or 0.0, -20.0, places=5)

    def test_missing_gps_is_explicit(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            path = _copy_fixture(Path(tmp) / "no_gps.jpg")
            exif_bytes = piexif.dump(
                {
                    "0th": {},
                    "Exif": {piexif.ExifIFD.DateTimeOriginal: b"2026:01:01 00:00:00"},
                    "GPS": {},
                    "1st": {},
                    "Interop": {},
                    "thumbnail": None,
                }
            )
            piexif.insert(exif_bytes, str(path))

            result = read_exif(path)

            self.assertEqual(result.gps_source, "missing")
            self.assertIsNone(result.gps_lat)
            self.assertIsNone(result.gps_lon)


def _copy_fixture(dest: Path) -> Path:
    fixture = Path(__file__).resolve().parent / "fixtures" / "exif" / "blank.jpg"
    dest.parent.mkdir(parents=True, exist_ok=True)
    shutil.copyfile(fixture, dest)
    return dest
