from fastapi import APIRouter, Depends
from sqlalchemy import text

from .db import get_db
from .mirror import enrich as mirror_enrich

# The virtual store API — PUBLIC read (it's the customer-facing storefront). Replaces
# WowPlatter's payload.php: same scene shape, but from Postgres instead of a full WP bootstrap.
router = APIRouter(prefix="/virtual", tags=["virtual-store"])

# Decal/logo assets were copied out of WP's uploads into webstore/assets (served at /store/assets)
# so the store is self-contained — no live-WP dependency. Rewrite the stored WP paths on the way out.
_WP_PREFIX = "/wp-content/uploads/"
_ASSET_BASE = "/store/assets/"


def _asset(u):
    return _ASSET_BASE + u[len(_WP_PREFIX):] if isinstance(u, str) and u.startswith(_WP_PREFIX) else u


async def _all(db, table, where="", params=None):
    rows = await db.execute(text(f"SELECT * FROM {table} {where}"), params or {})
    return [dict(r) for r in rows.mappings()]


def _attach(recs, meta):
    for r in recs:
        m = meta.get(r["release_id"], {})
        r["title"], r["artist"], r["thumb"] = m.get("title"), m.get("artist"), m.get("thumb")
    return recs


@router.get("/scene")
async def scene(space: int | None = None, db=Depends(get_db)):
    """Everything the renderer needs to draw ONE room. Pass ?space=<id> to load a linked room
    (the renderer calls it when you walk through a portal); default = the marked-default space.
    Records are SLIM — one front cover per crate; full per-crate list lazy-loads via /crate/{id}/records."""
    spaces = await _all(db, "virtual_space", "WHERE visible='y'")
    active = (next((s for s in spaces if s["id"] == space), None) if space else None) or next(
        (s for s in spaces if s.get("is_default") == "y"), spaces[0] if spaces else None)
    if not active:
        return {"space": None, "spaces": spaces, "racks": [], "crates": [], "records": []}
    sid = active["id"]
    if active.get("logo_texture"):
        active["logo_texture"] = _asset(active["logo_texture"])
    p = {"s": sid}

    # one front cover per crate, restricted to crates in THIS room
    recs = [dict(r) for r in (await db.execute(text("""
        SELECT DISTINCT ON (i.crate_id) i.crate_id, i.slot_number, i.release_id, i.sku, i.price, i.condition
        FROM inventory i JOIN virtual_crate c ON c.id = i.crate_id
        WHERE i.in_stock AND i.release_id IS NOT NULL AND c.space_id = :s
        ORDER BY i.crate_id, i.slot_number NULLS LAST"""), p)).mappings()]
    recs = _attach(recs, await mirror_enrich({r["release_id"] for r in recs}))

    decals = await _all(db, "virtual_decal",
                        "WHERE visible <> 'n' AND object_type='space' AND object_id = :s", p)
    for d in decals:
        d["image_url"], d["asset_url"] = _asset(d.get("image_url")), _asset(d.get("asset_url"))

    # links_to_id points at the PAIRED portal, not a space — resolve to the room it lives in.
    portals = [dict(r) for r in (await db.execute(text("""
        SELECT p.*, t.space_id AS to_space
        FROM virtual_portal p LEFT JOIN virtual_portal t ON t.id = p.links_to_id
        WHERE p.space_id = :s AND p.enabled = 'y'"""), p)).mappings()]

    return {
        "space": active, "spaces": spaces,
        "racks": await _all(db, "virtual_rack", "WHERE visible='y' AND space_id = :s", p),
        "rack_types": await _all(db, "virtual_rack_type"),
        "rack_type_levels": await _all(db, "virtual_rack_type_level"),
        "crates": await _all(db, "virtual_crate", "WHERE visible='y' AND space_id = :s", p),
        "crate_types": await _all(db, "virtual_crate_type"),
        "cameras": await _all(db, "virtual_camera", "WHERE space_id = :s", p),
        "lights": await _all(db, "virtual_light", "WHERE space_id = :s", p),
        "decals": decals,
        "portals": portals,
        "records": recs,
    }


@router.get("/crate/{crate_id}/records")
async def crate_records(crate_id: int, db=Depends(get_db)):
    """Full contents of one crate — loaded when the shopper starts digging it."""
    recs = [dict(r) for r in (await db.execute(text("""
        SELECT release_id, slot_number, sku, price, condition
        FROM inventory WHERE crate_id = :c AND in_stock AND release_id IS NOT NULL
        ORDER BY slot_number NULLS LAST"""), {"c": crate_id})).mappings()]
    recs = _attach(recs, await mirror_enrich({r["release_id"] for r in recs}))
    return {"ok": True, "crate_id": crate_id, "records": recs}


@router.get("/locate")
async def locate(release_id: int, db=Depends(get_db)):
    """Locate-stock: where in the physical store is this record? Returns the crate + position
    so the UI can fly the camera there and flash the bin."""
    row = (await db.execute(text("""
        SELECT i.crate_id, i.slot_number, i.sku,
               c.name, c.label_text, c.pos_x, c.pos_y, c.pos_z
        FROM inventory i LEFT JOIN virtual_crate c ON c.id = i.crate_id
        WHERE i.release_id = :r AND i.in_stock AND i.crate_id IS NOT NULL
        ORDER BY i.slot_number NULLS LAST LIMIT 1"""), {"r": release_id})).mappings().first()
    if not row:
        return {"ok": True, "located": False, "release_id": release_id}
    d = dict(row)
    return {
        "ok": True, "located": True, "release_id": release_id,
        "crate_id": d["crate_id"], "slot_number": d["slot_number"], "sku": d["sku"],
        "crate": {"name": d["name"], "label_text": d["label_text"],
                  "pos": [float(d["pos_x"] or 0), float(d["pos_y"] or 0), float(d["pos_z"] or 0)]},
    }
