"""Workspace API routes for VoIdea.""" from typing import Annotated from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.core.dependencies import get_current_user, get_db from app.models.user import User from app.schemas.workspace import ( AddMemberRequest, MemberResponse, WorkspaceCreate, WorkspaceResponse, WorkspaceUpdate, ) from app.services.workspace_service import WorkspaceError, WorkspaceLimitError, WorkspaceService router = APIRouter() def _ws_to_response(ws, member_count=0) -> WorkspaceResponse: return WorkspaceResponse( id=str(ws.id), name=ws.name, type=ws.type, owner_id=str(ws.owner_id), description=ws.description, member_count=member_count, created_at=ws.created_at, updated_at=ws.updated_at, ) @router.get("/", response_model=list[WorkspaceResponse]) async def list_workspaces( user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) personal = await svc.create_personal(user.id) workspaces = await svc.list_user_workspaces(user.id) result = [] for ws in workspaces: members = await svc.list_members(ws.id) result.append(_ws_to_response(ws, member_count=len(members))) return result @router.post("/", response_model=WorkspaceResponse, status_code=status.HTTP_201_CREATED) async def create_workspace( body: WorkspaceCreate, user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) try: if body.type == "personal": ws = await svc.create_personal(user.id, body.name) else: ws = await svc.create_team(body.name, user.id) members = await svc.list_members(ws.id) return _ws_to_response(ws, member_count=len(members)) except WorkspaceLimitError as e: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e)) except WorkspaceError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) @router.get("/{workspace_id}", response_model=WorkspaceResponse) async def get_workspace( workspace_id: str, user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) ws = await svc.get_by_id(UUID(workspace_id)) if not ws: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Workspace not found") membership = await svc.get_membership(ws.id, user.id) if not membership: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a member") members = await svc.list_members(ws.id) return _ws_to_response(ws, member_count=len(members)) @router.patch("/{workspace_id}", response_model=WorkspaceResponse) async def update_workspace( workspace_id: str, body: WorkspaceUpdate, user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) try: ws = await svc.update_workspace( UUID(workspace_id), user.id, body.model_dump(exclude_none=True) ) if not ws: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Workspace not found") members = await svc.list_members(ws.id) return _ws_to_response(ws, member_count=len(members)) except WorkspaceError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) @router.delete("/{workspace_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_workspace( workspace_id: str, user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) try: success = await svc.delete_workspace(UUID(workspace_id), user.id) if not success: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Workspace not found") except WorkspaceError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) # ── Members ── @router.get("/{workspace_id}/members", response_model=list[MemberResponse]) async def list_members( workspace_id: str, user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) ws = await svc.get_by_id(UUID(workspace_id)) if not ws: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Workspace not found") membership = await svc.get_membership(ws.id, user.id) if not membership: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a member") members = await svc.list_members(ws.id) return [MemberResponse(**m) for m in members] @router.post("/{workspace_id}/members", response_model=MemberResponse, status_code=status.HTTP_201_CREATED) async def add_member( workspace_id: str, body: AddMemberRequest, user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) try: member = await svc.add_member(UUID(workspace_id), UUID(body.user_id), body.role) members = await svc.list_members(ws_id := UUID(workspace_id)) m = next((m for m in members if m["id"] == str(member.id)), None) return MemberResponse(**m) if m else MemberResponse( id=str(member.id), user_id=str(member.user_id), workspace_id=str(member.workspace_id), role=member.role, joined_at=member.created_at, ) except WorkspaceLimitError as e: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e)) except WorkspaceError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) @router.delete("/{workspace_id}/members/{member_id}", status_code=status.HTTP_204_NO_CONTENT) async def remove_member( workspace_id: str, member_id: str, user: Annotated[User, Depends(get_current_user)], db: AsyncSession = Depends(get_db), ): svc = WorkspaceService(db) success = await svc.remove_member(UUID(workspace_id), UUID(member_id)) if not success: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Member not found")