from __future__ import annotations

from dataclasses import dataclass

from src.errors import UserError


@dataclass(frozen=True)
class SelectedPhoto:
    photo_sha256: str
    gbp_jpg_path: str
    confidence: float


def select_photos(
    *, records: list[dict[str, object]], deal_id: int, max_photos: int
) -> list[SelectedPhoto]:
    if int(max_photos) <= 0:
        raise UserError("Invalid max_photos.")

    filtered = [_to_candidate(r) for r in records if _as_int(r.get("deal_id")) == int(deal_id)]
    available = [c for c in filtered if c is not None]
    available.sort(key=lambda c: (-c.confidence, c.photo_sha256))
    selected = available[: int(max_photos)]
    if not selected:
        raise UserError(
            f"No GBP-ready photos found for deal_id={deal_id}. Run export gbp + run audit first."
        )
    return selected


def _to_candidate(record: dict[str, object]) -> SelectedPhoto | None:
    if record.get("gbp_jpg_exists") is not True:
        return None
    path = _as_str(record.get("gbp_jpg_path"))
    digest = _as_str(record.get("photo_sha256"))
    conf = _as_float(record.get("confidence")) or 0.0
    if not path or not digest:
        return None
    return SelectedPhoto(photo_sha256=digest, gbp_jpg_path=path, confidence=conf)


def _as_int(value: object) -> int | None:
    if isinstance(value, int):
        return value
    if isinstance(value, str) and value.isdigit():
        return int(value)
    return None


def _as_float(value: object) -> float | None:
    if isinstance(value, (int, float)):
        return float(value)
    if isinstance(value, str) and value.strip():
        try:
            return float(value.strip())
        except ValueError:
            return None
    return None


def _as_str(value: object) -> str:
    return "" if value is None else str(value)
