import logging from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException from starlette import status from api.dependency import get_dao from api.endpoints.auth import get_current_user from api.models.EnvironmentRequest import EnvironmentCreate, EnvironmentUpdate, AssetToEnvironment, AssetsToEnvironment from models.Environment import Environment from repos.dao import DAO logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/environments", tags=["Environments"], dependencies=[Depends(get_current_user)]) async def check_character_access(character_id: str, current_user: dict, dao: DAO): character = await dao.chars.get_character(character_id) if not character: raise HTTPException(status_code=404, detail="Character not found") is_creator = character.created_by == str(current_user["_id"]) is_project_member = False if character.project_id and character.project_id in current_user.get("project_ids", []): is_project_member = True if not is_creator and not is_project_member: raise HTTPException(status_code=403, detail="Access denied to character") return character @router.post("/", response_model=Environment) async def create_environment( env_req: EnvironmentCreate, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): logger.info(f"Creating environment '{env_req.name}' for character {env_req.character_id}") await check_character_access(env_req.character_id, current_user, dao) # Verify assets exist if provided if env_req.asset_ids: for aid in env_req.asset_ids: asset = await dao.assets.get_asset(aid) if not asset: raise HTTPException(status_code=400, detail=f"Asset {aid} not found") new_env = Environment(**env_req.model_dump()) created_env = await dao.environments.create_env(new_env) return created_env @router.get("/character/{character_id}", response_model=List[Environment]) async def get_character_environments( character_id: str, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): logger.info(f"Getting environments for character {character_id}") await check_character_access(character_id, current_user, dao) return await dao.environments.get_character_envs(character_id) @router.get("/{env_id}", response_model=Environment) async def get_environment( env_id: str, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): env = await dao.environments.get_env(env_id) if not env: raise HTTPException(status_code=404, detail="Environment not found") await check_character_access(env.character_id, current_user, dao) return env @router.put("/{env_id}", response_model=Environment) async def update_environment( env_id: str, env_update: EnvironmentUpdate, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): env = await dao.environments.get_env(env_id) if not env: raise HTTPException(status_code=404, detail="Environment not found") await check_character_access(env.character_id, current_user, dao) update_data = env_update.model_dump(exclude_unset=True) if not update_data: return env success = await dao.environments.update_env(env_id, update_data) if not success: raise HTTPException(status_code=500, detail="Failed to update environment") return await dao.environments.get_env(env_id) @router.delete("/{env_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_environment( env_id: str, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): env = await dao.environments.get_env(env_id) if not env: raise HTTPException(status_code=404, detail="Environment not found") await check_character_access(env.character_id, current_user, dao) success = await dao.environments.delete_env(env_id) if not success: raise HTTPException(status_code=500, detail="Failed to delete environment") return None @router.post("/{env_id}/assets", status_code=status.HTTP_200_OK) async def add_asset_to_environment( env_id: str, req: AssetToEnvironment, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): env = await dao.environments.get_env(env_id) if not env: raise HTTPException(status_code=404, detail="Environment not found") await check_character_access(env.character_id, current_user, dao) # Verify asset exists asset = await dao.assets.get_asset(req.asset_id) if not asset: raise HTTPException(status_code=404, detail="Asset not found") success = await dao.environments.add_asset(env_id, req.asset_id) return {"success": success} @router.post("/{env_id}/assets/batch", status_code=status.HTTP_200_OK) async def add_assets_batch_to_environment( env_id: str, req: AssetsToEnvironment, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): env = await dao.environments.get_env(env_id) if not env: raise HTTPException(status_code=404, detail="Environment not found") await check_character_access(env.character_id, current_user, dao) # Verify all assets exist assets = await dao.assets.get_assets_by_ids(req.asset_ids) if len(assets) != len(req.asset_ids): found_ids = {a.id for a in assets} missing_ids = [aid for aid in req.asset_ids if aid not in found_ids] raise HTTPException(status_code=404, detail=f"Some assets not found: {missing_ids}") success = await dao.environments.add_assets(env_id, req.asset_ids) return {"success": success} @router.delete("/{env_id}/assets/{asset_id}", status_code=status.HTTP_200_OK) async def remove_asset_from_environment( env_id: str, asset_id: str, dao: DAO = Depends(get_dao), current_user: dict = Depends(get_current_user) ): env = await dao.environments.get_env(env_id) if not env: raise HTTPException(status_code=404, detail="Environment not found") await check_character_access(env.character_id, current_user, dao) success = await dao.environments.remove_asset(env_id, asset_id) return {"success": success}