"""OG preview endpoints.

GET /activity/{activity_id}
    Returns a minimal HTML page with Open Graph meta tags for social link
    previews (Telegram, WhatsApp, Slack, …).  nginx proxies only bot
    User-Agents here; regular browsers still get the static SPA shell.

GET /api/og-image/{user_handle}/{activity_id}.png
    Returns the pre-generated 400×400 track PNG.  Falls back to generating
    on the fly if the static file doesn't exist yet (e.g. a brand-new import
    before the next deploy-time generation run).
"""

from __future__ import annotations

import html
import json
from datetime import datetime, timezone
from pathlib import Path

from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse, Response

from bincio.serve import deps

router = APIRouter()

# Sport → emoji map (extend as needed)
_SPORT_EMOJI: dict[str, str] = {
    "cycling":     "🚴",
    "running":     "🏃",
    "swimming":    "🏊",
    "hiking":      "🥾",
    "walking":     "🚶",
    "skiing":      "⛷️",
    "rowing":      "🚣",
    "triathlon":   "🏊",
    "e_cycling":   "🚴",
    "gravel":      "🚴",
}


def _find_user(data_dir: Path, activity_id: str) -> str | None:
    """Return the user handle that owns *activity_id*, or None."""
    for user_dir in sorted(data_dir.iterdir()):
        if not user_dir.is_dir() or user_dir.name.startswith("_") or user_dir.name == "segments":
            continue
        if (user_dir / "activities" / f"{activity_id}.json").exists():
            return user_dir.name
    return None


def _fmt_description(detail: dict, handle: str) -> str:
    parts: list[str] = []
    sport   = (detail.get("sport") or "").lower()
    emoji   = _SPORT_EMOJI.get(sport, "🏅")
    parts.append(emoji)

    dist_m = detail.get("distance_m")
    if dist_m:
        parts.append(f"{dist_m / 1000:.1f} km")

    gain = detail.get("elevation_gain_m")
    if gain:
        parts.append(f"{gain:.0f} m ↑")

    dur = detail.get("moving_time_s") or detail.get("duration_s")
    if dur:
        h, rem = divmod(int(dur), 3600)
        m = rem // 60
        parts.append(f"{h}h {m:02d}m" if h else f"{m}m")

    started = detail.get("started_at")
    if started:
        try:
            dt = datetime.fromisoformat(started).astimezone(timezone.utc)
            parts.append(dt.strftime("%-d %b %Y"))
        except ValueError:
            pass

    parts.append(f"@{handle}")
    return " · ".join(parts)


@router.get("/activity/{activity_id}", response_class=HTMLResponse, include_in_schema=False)
@router.get("/activity/{activity_id}/", response_class=HTMLResponse, include_in_schema=False)
async def og_preview(activity_id: str, request: Request) -> HTMLResponse:
    data_dir = deps._get_data_dir()
    handle   = _find_user(data_dir, activity_id)
    if handle is None:
        raise HTTPException(404)

    json_path = data_dir / handle / "activities" / f"{activity_id}.json"
    detail    = json.loads(json_path.read_text(encoding="utf-8"))

    title   = detail.get("title") or activity_id
    desc    = _fmt_description(detail, handle)
    base    = str(request.base_url).rstrip("/")
    img_url = f"{base}/og-image/{handle}/{activity_id}.png"
    act_url = f"{base}/activity/{activity_id}/"

    h_title = html.escape(title)
    h_desc  = html.escape(desc)
    h_img   = html.escape(img_url)
    h_url   = html.escape(act_url)

    content = f"""<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>{h_title} – BincioActivity</title>
  <meta property="og:title"       content="{h_title}" />
  <meta property="og:description" content="{h_desc}" />
  <meta property="og:image"       content="{h_img}" />
  <meta property="og:image:width" content="400" />
  <meta property="og:image:height" content="400" />
  <meta property="og:url"         content="{h_url}" />
  <meta property="og:type"        content="website" />
  <meta property="og:site_name"   content="BincioActivity" />
  <meta name="twitter:card"       content="summary" />
  <meta name="twitter:title"      content="{h_title}" />
  <meta name="twitter:description" content="{h_desc}" />
  <meta name="twitter:image"      content="{h_img}" />
  <script>window.location.replace("{act_url}");</script>
</head>
<body></body>
</html>"""
    return HTMLResponse(content=content)


@router.get("/og-image/{user_handle}/{activity_id}.png", include_in_schema=False)
async def og_image(user_handle: str, activity_id: str) -> Response:
    data_dir  = deps._get_data_dir()
    www_root  = Path("/var/www/activity")
    img_path  = www_root / "og-image" / user_handle / f"{activity_id}.png"

    if img_path.exists():
        return Response(
            content=img_path.read_bytes(),
            media_type="image/png",
            headers={"Cache-Control": "public, max-age=86400"},
        )

    # Fallback: generate on the fly (e.g. new activity before next deploy run)
    ts_path = data_dir / user_handle / "activities" / f"{activity_id}.timeseries.json"
    if not ts_path.exists():
        raise HTTPException(404)

    try:
        from bincio.render.ogimage import generate_for_activity
        png = generate_for_activity(ts_path)
        img_path.parent.mkdir(parents=True, exist_ok=True)
        img_path.write_bytes(png)
    except Exception:
        raise HTTPException(500, "Image generation failed")

    return Response(
        content=png,
        media_type="image/png",
        headers={"Cache-Control": "public, max-age=86400"},
    )
