from __future__ import annotations from fastapi import APIRouter, Depends, Request from fastapi.responses import JSONResponse from app_dependencies import get_runtime from card_store import normalize_card_id from route_helpers import read_json_request from session_store import normalize_session_chat_id from web_runtime import WebAppRuntime from workbench_store import ( delete_workbench_item, load_workbench_items, persist_workbench_item, promote_workbench_item, ) router = APIRouter() @router.get("/workbench") async def get_workbench(request: Request) -> JSONResponse: chat_id = normalize_session_chat_id(str(request.query_params.get("chat_id", "web") or "web")) if not chat_id: return JSONResponse({"error": "invalid chat id"}, status_code=400) try: items = load_workbench_items(chat_id) except ValueError as exc: return JSONResponse({"error": str(exc)}, status_code=400) except Exception as exc: return JSONResponse({"error": f"failed to load workbench: {exc}"}, status_code=500) return JSONResponse({"items": items}) @router.post("/workbench") async def upsert_workbench( request: Request, runtime: WebAppRuntime = Depends(get_runtime), ) -> JSONResponse: try: payload = await read_json_request(request) except ValueError as exc: return JSONResponse({"error": str(exc)}, status_code=400) item = persist_workbench_item(payload) if item is None: return JSONResponse({"error": "invalid workbench payload"}, status_code=400) await runtime.publish_workbench_changed(item.get("chat_id")) return JSONResponse({"status": "ok", "item": item}, status_code=201) @router.delete("/workbench/{item_id}") async def delete_workbench( item_id: str, request: Request, runtime: WebAppRuntime = Depends(get_runtime), ) -> JSONResponse: chat_id = normalize_session_chat_id(str(request.query_params.get("chat_id", "web") or "web")) if not chat_id: return JSONResponse({"error": "invalid chat id"}, status_code=400) if not normalize_card_id(item_id): return JSONResponse({"error": "invalid workbench item id"}, status_code=400) try: removed = delete_workbench_item(chat_id, item_id) except ValueError as exc: return JSONResponse({"error": str(exc)}, status_code=400) except Exception as exc: return JSONResponse({"error": f"failed to delete workbench item: {exc}"}, status_code=500) if not removed: return JSONResponse({"error": "workbench item not found"}, status_code=404) await runtime.publish_workbench_changed(chat_id) return JSONResponse({"status": "ok", "item_id": item_id}) @router.post("/workbench/{item_id}/promote") async def promote_workbench( item_id: str, request: Request, runtime: WebAppRuntime = Depends(get_runtime), ) -> JSONResponse: try: payload = await read_json_request(request) except ValueError: payload = {} chat_id = normalize_session_chat_id(str(payload.get("chat_id", "web") or "web")) if not chat_id: return JSONResponse({"error": "invalid chat id"}, status_code=400) if not normalize_card_id(item_id): return JSONResponse({"error": "invalid workbench item id"}, status_code=400) try: card = promote_workbench_item(chat_id, item_id) except FileNotFoundError: return JSONResponse({"error": "workbench item not found"}, status_code=404) except ValueError as exc: return JSONResponse({"error": str(exc)}, status_code=400) except RuntimeError as exc: return JSONResponse({"error": str(exc)}, status_code=500) except Exception as exc: return JSONResponse({"error": f"failed to promote workbench item: {exc}"}, status_code=500) await runtime.publish_workbench_changed(chat_id) await runtime.publish_cards_changed(card.get("chat_id")) return JSONResponse({"status": "ok", "card": card, "item_id": item_id})