import uuid from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import get_db_session, require_study_member, require_study_roles from app.crud import member as member_crud from app.crud import study as study_crud from app.schemas.member import StudyMemberCreate, StudyMemberRead, StudyMemberUpdate router = APIRouter() async def _ensure_study_exists(db: AsyncSession, study_id: uuid.UUID): study = await study_crud.get(db, study_id) if not study: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Study not found") return study @router.post( "/", response_model=StudyMemberRead, status_code=status.HTTP_201_CREATED, dependencies=[Depends(require_study_roles(["PM"]))], ) async def add_member( study_id: uuid.UUID, member_in: StudyMemberCreate, db: AsyncSession = Depends(get_db_session), ) -> StudyMemberRead: await _ensure_study_exists(db, study_id) existing = await member_crud.get_member(db, study_id, member_in.user_id) if existing: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Member already exists") member = await member_crud.add_member(db, study_id, member_in) return member @router.get( "/", response_model=list[StudyMemberRead], dependencies=[Depends(require_study_member())], ) async def list_members( study_id: uuid.UUID, skip: int = 0, limit: int = 100, db: AsyncSession = Depends(get_db_session), ) -> list[StudyMemberRead]: await _ensure_study_exists(db, study_id) members = await member_crud.list_members(db, study_id, skip=skip, limit=limit) return list(members) @router.patch( "/{member_id}", response_model=StudyMemberRead, dependencies=[Depends(require_study_roles(["PM"]))], ) async def update_member( study_id: uuid.UUID, member_id: uuid.UUID, member_in: StudyMemberUpdate, db: AsyncSession = Depends(get_db_session), ) -> StudyMemberRead: await _ensure_study_exists(db, study_id) member = await member_crud.get_member_by_id(db, member_id) if not member or member.study_id != study_id: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Member not found") updated = await member_crud.update_member(db, member, member_in) return updated @router.delete( "/{member_id}", response_model=StudyMemberRead, dependencies=[Depends(require_study_roles(["PM"]))], ) async def remove_member( study_id: uuid.UUID, member_id: uuid.UUID, db: AsyncSession = Depends(get_db_session), ) -> StudyMemberRead: await _ensure_study_exists(db, study_id) member = await member_crud.get_member_by_id(db, member_id) if not member or member.study_id != study_id: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Member not found") removed = await member_crud.remove_member(db, member) return removed