"""Pexels photo search and download service.

Searches the Pexels API for brand-aligned photos, downloads them at
appropriate resolutions, and caches results to stay within rate limits.

Requires ``PEXELS_API_KEY`` environment variable. Falls back gracefully
when missing (raises ``PexelsUnavailableError``).
"""

from __future__ import annotations

import hashlib
import json
import logging
import os
from dataclasses import asdict, dataclass
from pathlib import Path

from tokenmoulds._compat import HAS_HTTPX, httpx, strip_hex
from tokenmoulds.media.color_grade import smush_photo
from tokenmoulds.settings import get_settings


logger = logging.getLogger(__name__)

_API_BASE = "https://api.pexels.com/v1"


@dataclass(frozen=True, slots=True)
class ResolutionPreset:
    """Image size and compression settings for a specific context."""

    max_width: int
    max_height: int
    quality: int


RESOLUTION_PRESETS: dict[str, ResolutionPreset] = {
    "presentation_bg": ResolutionPreset(1920, 1080, 82),
    "document_hero": ResolutionPreset(1200, 800, 80),
    "document_inline": ResolutionPreset(800, 600, 78),
    "thumbnail": ResolutionPreset(400, 300, 72),
}


class PexelsUnavailableError(RuntimeError):
    """Raised when Pexels API is not available (missing key or httpx)."""


@dataclass(frozen=True, slots=True)
class PhotoResult:
    """A single photo result from Pexels."""

    id: int
    url: str
    photographer: str
    photographer_url: str
    width: int
    height: int
    avg_color: str


def is_available() -> bool:
    """Check if the Pexels service can be used (httpx installed + API key set)."""
    return HAS_HTTPX and bool(os.environ.get("PEXELS_API_KEY", ""))


def _api_key() -> str:
    key = os.environ.get("PEXELS_API_KEY", "")
    if not key:
        msg = "PEXELS_API_KEY environment variable is not set"
        raise PexelsUnavailableError(msg)
    return key


def _cache_dir() -> Path:
    return get_settings().cache_subdir("photos")


def _cache_key(query: str, color: str | None, orientation: str) -> str:
    raw = f"{query}|{color or ''}|{orientation}"
    return hashlib.sha256(raw.encode()).hexdigest()[:16]


def search_photos(
    query: str,
    *,
    color: str | None = None,
    orientation: str = "landscape",
    per_page: int = 5,
) -> list[PhotoResult]:
    """Search Pexels for photos matching query and optional color filter.

    Args:
        query: Search terms (e.g. "modern office architecture").
        color: Hex color filter without ``#`` (e.g. "2563EB").
        orientation: One of "landscape", "portrait", "square".
        per_page: Number of results (1-80).

    Returns:
        List of ``PhotoResult`` objects.

    Raises:
        PexelsUnavailableError: If API key or httpx is missing.
    """
    if not HAS_HTTPX:
        msg = "httpx is required for Pexels integration"
        raise PexelsUnavailableError(msg)

    key = _api_key()

    # Check search result cache
    ck = _cache_key(query, color, orientation)
    cache_file = _cache_dir() / f"{ck}.json"
    try:
        data = json.loads(cache_file.read_text())
        return [PhotoResult(**r) for r in data]
    except FileNotFoundError:
        pass

    params: dict[str, str | int] = {
        "query": query,
        "orientation": orientation,
        "per_page": per_page,
    }
    if color:
        params["color"] = strip_hex(color)

    try:
        resp = httpx.get(
            f"{_API_BASE}/search",
            headers={"Authorization": key},
            params=params,
            timeout=15.0,
        )
        resp.raise_for_status()
    except Exception as exc:
        msg = f"Pexels API request failed: {exc}"
        raise PexelsUnavailableError(msg) from exc
    body = resp.json()

    results: list[PhotoResult] = []
    for photo in body.get("photos", []):
        src = photo.get("src", {})
        results.append(
            PhotoResult(
                id=photo["id"],
                url=src.get("large2x", src.get("original", "")),
                photographer=photo.get("photographer", ""),
                photographer_url=photo.get("photographer_url", ""),
                width=photo.get("width", 0),
                height=photo.get("height", 0),
                avg_color=photo.get("avg_color", ""),
            )
        )

    # Cache results
    cache_file.write_text(json.dumps([asdict(r) for r in results]))
    return results


def download_photo(
    url: str,
    *,
    preset: str = "presentation_bg",
) -> bytes:
    """Download a photo and smush it for template embedding.

    Args:
        url: Direct image URL from Pexels.
        preset: Resolution preset name (see ``RESOLUTION_PRESETS``).

    Returns:
        Compressed JPEG bytes ready for embedding.

    Raises:
        PexelsUnavailableError: If httpx is missing.
    """
    if not HAS_HTTPX:
        msg = "httpx is required for Pexels integration"
        raise PexelsUnavailableError(msg)

    # Check image cache
    url_hash = hashlib.sha256(url.encode()).hexdigest()[:16]
    cache_file = _cache_dir() / f"{url_hash}_{preset}.jpg"
    try:
        return cache_file.read_bytes()
    except FileNotFoundError:
        pass

    try:
        resp = httpx.get(url, timeout=30.0, follow_redirects=True)
        resp.raise_for_status()
    except Exception as exc:
        msg = f"Photo download failed: {exc}"
        raise PexelsUnavailableError(msg) from exc

    res = RESOLUTION_PRESETS.get(preset, RESOLUTION_PRESETS["presentation_bg"])
    compressed = smush_photo(
        resp.content,
        max_width=res.max_width,
        max_height=res.max_height,
        quality=res.quality,
    )

    cache_file.write_bytes(compressed)
    return compressed
