"""Command config management API routes.""" import logging from typing import Any from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession from ..auth.dependencies import get_current_user from ..database.engine import get_session from ..database.models import CommandConfig, CommandTracker, User _LOGGER = logging.getLogger(__name__) router = APIRouter(prefix="/api/command-configs", tags=["command-configs"]) class CommandConfigCreate(BaseModel): provider_type: str name: str icon: str = "" enabled_commands: list[str] = [] locale: str = "en" response_mode: str = "media" default_count: int = 5 rate_limits: dict[str, Any] = {} class CommandConfigUpdate(BaseModel): name: str | None = None icon: str | None = None enabled_commands: list[str] | None = None locale: str | None = None response_mode: str | None = None default_count: int | None = None rate_limits: dict[str, Any] | None = None @router.get("") async def list_command_configs( user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """List all command configs for the current user.""" result = await session.exec( select(CommandConfig).where(CommandConfig.user_id == user.id) ) return [_config_response(c) for c in result.all()] @router.post("", status_code=status.HTTP_201_CREATED) async def create_command_config( body: CommandConfigCreate, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Create a new command config.""" # Validate provider_type valid_types = ("immich",) if body.provider_type not in valid_types: raise HTTPException( status_code=400, detail=f"Invalid provider_type. Must be one of: {', '.join(valid_types)}", ) config = CommandConfig(user_id=user.id, **body.model_dump()) session.add(config) await session.commit() await session.refresh(config) return _config_response(config) @router.get("/{config_id}") async def get_command_config( config_id: int, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Get a single command config.""" config = await _get_user_config(session, config_id, user.id) return _config_response(config) @router.put("/{config_id}") async def update_command_config( config_id: int, body: CommandConfigUpdate, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Update a command config.""" config = await _get_user_config(session, config_id, user.id) for field, value in body.model_dump(exclude_unset=True).items(): setattr(config, field, value) session.add(config) await session.commit() await session.refresh(config) return _config_response(config) @router.delete("/{config_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_command_config( config_id: int, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Delete a command config. Fails if in use by any command tracker.""" config = await _get_user_config(session, config_id, user.id) # Check if any command tracker references this config result = await session.exec( select(CommandTracker).where(CommandTracker.command_config_id == config_id) ) if result.first(): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Cannot delete: command config is in use by a command tracker", ) await session.delete(config) await session.commit() # --- Helpers --- def _config_response(c: CommandConfig) -> dict: return { "id": c.id, "user_id": c.user_id, "provider_type": c.provider_type, "name": c.name, "icon": c.icon, "enabled_commands": c.enabled_commands or [], "locale": c.locale, "response_mode": c.response_mode, "default_count": c.default_count, "rate_limits": c.rate_limits or {}, "created_at": c.created_at.isoformat(), } async def _get_user_config( session: AsyncSession, config_id: int, user_id: int ) -> CommandConfig: config = await session.get(CommandConfig, config_id) if not config or config.user_id != user_id: raise HTTPException(status_code=404, detail="Command config not found") return config