"""EXIF date extraction via exiftool."""

from __future__ import annotations

from dataclasses import dataclass

import datetime
import json
import logging
import pathlib
import subprocess

logger = logging.getLogger(__name__)


@dataclass
class Metadata:
    """Extracted metadata for a single file."""

    source_path: pathlib.Path
    date_taken: datetime.datetime | None = None
    date_from_mtime: bool = False


# Date tag lookup order (first match wins)
_DATE_TAGS = [
    "EXIF:DateTimeOriginal",
    "EXIF:CreateDate",
    "QuickTime:CreateDate",
    "QuickTime:MediaCreateDate",
    "XMP:DateTimeOriginal",
    "XMP:CreateDate",
]

_DATE_FORMAT = "%Y:%m:%d %H:%M:%S"


def _run_exiftool(paths: list[pathlib.Path]) -> list[dict[str, object]]:
    """Run exiftool and return parsed JSON output."""
    cmd = [
        "exiftool",
        "-json",
        "-n",  # numeric output (no conversion for GPS etc.)
        "-G",  # group names in tags
        *[str(p) for p in paths],
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if not result.stdout.strip():
        return []
    return json.loads(result.stdout)


def _parse_date(raw: dict[str, object]) -> datetime.datetime | None:
    """Extract date from the first available date tag."""
    for tag in _DATE_TAGS:
        value = raw.get(tag)
        if not value or not isinstance(value, str):
            continue
        try:
            dt = datetime.datetime.strptime(value, _DATE_FORMAT)
            # Reject placeholder dates like 0000:00:00
            if dt.year < 1900:
                continue
            return dt
        except ValueError:
            continue
    return None


def _parse_one(raw: dict[str, object], path: pathlib.Path) -> Metadata:
    """Parse a single exiftool JSON result into a Metadata object."""
    date_taken = _parse_date(raw)
    date_from_mtime = False
    if date_taken is None and path.exists():
        mtime = path.stat().st_mtime
        date_taken = datetime.datetime.fromtimestamp(mtime)
        date_from_mtime = True
    return Metadata(
        source_path=path,
        date_taken=date_taken,
        date_from_mtime=date_from_mtime,
    )


def extract(path: pathlib.Path) -> Metadata:
    """Extract metadata from a single file."""
    results = _run_exiftool([path])
    if not results:
        return Metadata(source_path=path)
    return _parse_one(results[0], path)


def extract_batch(
    paths: list[pathlib.Path], batch_size: int = 100
) -> dict[pathlib.Path, Metadata]:
    """Extract metadata from multiple files, calling exiftool in batches."""
    if not paths:
        return {}
    out: dict[pathlib.Path, Metadata] = {}
    total = len(paths)
    for i in range(0, total, batch_size):
        chunk = paths[i : i + batch_size]
        logger.info(f"Extracting metadata ... {min(i + batch_size, total)}/{total}")
        results = _run_exiftool(chunk)
        for raw in results:
            source = pathlib.Path(str(raw.get("SourceFile", "")))
            out[source] = _parse_one(raw, source)
    return out
