"""Serve cached DOM.РФ photos.

GET /api/v1/photos/{obj_id}/{file_id}?size=thumb|full

- size=thumb (default): WebP 320×240 from local cache. If neither the thumb
  nor the original exists locally, lazily fetch the original from DOM.РФ via
  httpx + Basic auth, save it, generate the thumb, and serve. Subsequent
  requests hit the disk cache and return in single-digit ms.
- size=full: original PNG/JPG if cached locally, else 302 redirect upstream.
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Annotated, Literal

import httpx
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import FileResponse, RedirectResponse, Response
from sqlalchemy import text
from sqlalchemy.orm import Session

from app.core.db import get_db
from app.services.photos.thumbs import make_thumbnail

logger = logging.getLogger(__name__)
router = APIRouter()

# Same Basic auth used by the kn-API scraper (1:qwe). Photo CDN accepts it
# without the TLS-fingerprint challenge that the JSON API enforces.
_UPSTREAM_AUTH = "Basic MTpxd2U="
_UPSTREAM_TIMEOUT = httpx.Timeout(8.0, connect=4.0)
_PHOTOS_DIR = Path("data/raw/domrf_photos")

_PHOTO_LOOKUP_SQL = text(
    """
    SELECT local_path, thumb_path, photo_url, photo_name, size_bytes
      FROM domrf_kn_photos
     WHERE obj_id = :obj AND obj_file_id = :fid
     LIMIT 1
    """
)


def _ext_from_name(name: str | None) -> str:
    n = (name or "").lower()
    return ".jpg" if n.endswith((".jpg", ".jpeg")) else ".png"


def _fetch_upstream(url: str) -> bytes | None:
    """Fetch upstream image bytes. Returns None on any error (logged)."""
    try:
        with httpx.Client(timeout=_UPSTREAM_TIMEOUT, follow_redirects=True) as c:
            r = c.get(url, headers={"Authorization": _UPSTREAM_AUTH})
            if r.status_code == 200 and r.content:
                return r.content
            logger.warning("upstream photo %s status=%s len=%s", url, r.status_code, len(r.content))
    except Exception as e:
        logger.warning("upstream photo %s failed: %s", url, e)
    return None


def _persist_original(obj_id: int, file_id: str, name: str | None, data: bytes) -> Path:
    obj_dir = _PHOTOS_DIR / str(obj_id)
    obj_dir.mkdir(parents=True, exist_ok=True)
    dst = obj_dir / f"{file_id}{_ext_from_name(name)}"
    dst.write_bytes(data)
    return dst


@router.get("/{obj_id}/{file_id}")
def get_photo(
    db: Annotated[Session, Depends(get_db)],
    obj_id: int,
    file_id: str,
    size: Annotated[Literal["thumb", "full"], Query()] = "thumb",
) -> Response:
    row = db.execute(_PHOTO_LOOKUP_SQL, {"obj": obj_id, "fid": file_id}).mappings().first()
    if not row:
        raise HTTPException(status_code=404, detail="photo not registered")

    local_path = row["local_path"]
    thumb_path = row["thumb_path"]
    upstream = row["photo_url"]
    photo_name = row["photo_name"]

    headers = {"Cache-Control": "public, max-age=604800, immutable"}

    # ── size=thumb ──────────────────────────────────────────────────────────
    if size == "thumb":
        if thumb_path and Path(thumb_path).exists():
            return FileResponse(thumb_path, media_type="image/webp", headers=headers)

        # Generate from existing local original.
        if local_path and Path(local_path).exists():
            generated = make_thumbnail(Path(local_path))
            if generated and generated.exists():
                db.execute(
                    text(
                        "UPDATE domrf_kn_photos SET thumb_path = :tp"
                        " WHERE obj_id = :o AND obj_file_id = :f"
                    ),
                    {"tp": str(generated), "o": obj_id, "f": file_id},
                )
                db.commit()
                return FileResponse(str(generated), media_type="image/webp", headers=headers)

        # Lazy: fetch upstream, persist original + thumb, then serve.
        if upstream:
            data = _fetch_upstream(upstream)
            if data:
                src = _persist_original(obj_id, file_id, photo_name, data)
                # Record the original immediately so a failed thumbnail does not
                # orphan the on-disk file and trigger an eternal re-fetch.
                db.execute(
                    text(
                        "UPDATE domrf_kn_photos"
                        " SET local_path = :lp, downloaded_at = NOW()"
                        " WHERE obj_id = :o AND obj_file_id = :f"
                    ),
                    {"lp": str(src), "o": obj_id, "f": file_id},
                )
                db.commit()
                generated = make_thumbnail(src)
                if generated and generated.exists():
                    db.execute(
                        text(
                            "UPDATE domrf_kn_photos SET thumb_path = :tp"
                            " WHERE obj_id = :o AND obj_file_id = :f"
                        ),
                        {"tp": str(generated), "o": obj_id, "f": file_id},
                    )
                    db.commit()
                    return FileResponse(str(generated), media_type="image/webp", headers=headers)

        # Last resort — redirect to upstream so the page still renders.
        if upstream:
            return RedirectResponse(upstream, status_code=302)
        raise HTTPException(status_code=404, detail="photo unavailable")

    # ── size=full ───────────────────────────────────────────────────────────
    if local_path and Path(local_path).exists():
        return FileResponse(local_path, headers=headers)
    if upstream:
        return RedirectResponse(upstream, status_code=302)
    raise HTTPException(status_code=404, detail="full photo unavailable")
