"""Organizations router: list, create, membership.""" from __future__ import annotations import uuid from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.models.user import DuniterIdentity from app.schemas.organization import OrgMemberOut, OrganizationCreate, OrganizationOut from app.services.auth_service import get_current_identity from app.services.org_service import ( add_member, create_organization, get_organization, get_organization_by_slug, is_member, list_members, list_organizations, ) router = APIRouter() @router.get("/", response_model=list[OrganizationOut]) async def get_organizations(db: AsyncSession = Depends(get_db)) -> list[OrganizationOut]: """List all organizations (public — transparent ones need no auth).""" orgs = await list_organizations(db) return [OrganizationOut.model_validate(o) for o in orgs] @router.post("/", response_model=OrganizationOut, status_code=status.HTTP_201_CREATED) async def post_organization( payload: OrganizationCreate, db: AsyncSession = Depends(get_db), identity: DuniterIdentity = Depends(get_current_identity), ) -> OrganizationOut: """Create a new organization (authenticated users only).""" existing = await get_organization_by_slug(db, payload.slug) if existing: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=f"Slug '{payload.slug}' déjà utilisé", ) org = await create_organization(db, **payload.model_dump()) # Creator becomes admin await add_member(db, org.id, identity.id, role="admin") return OrganizationOut.model_validate(org) @router.get("/{org_id}", response_model=OrganizationOut) async def get_organization_detail( org_id: uuid.UUID, db: AsyncSession = Depends(get_db) ) -> OrganizationOut: org = await get_organization(db, org_id) if not org: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Organisation introuvable") return OrganizationOut.model_validate(org) @router.get("/{org_id}/members", response_model=list[OrgMemberOut]) async def get_members( org_id: uuid.UUID, db: AsyncSession = Depends(get_db), identity: DuniterIdentity = Depends(get_current_identity), ) -> list[OrgMemberOut]: org = await get_organization(db, org_id) if not org: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Organisation introuvable") if not org.is_transparent and not await is_member(db, org_id, identity.id): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Accès refusé") members = await list_members(db, org_id) return [OrgMemberOut.model_validate(m) for m in members] @router.post("/{org_id}/join", response_model=OrgMemberOut, status_code=status.HTTP_201_CREATED) async def join_organization( org_id: uuid.UUID, db: AsyncSession = Depends(get_db), identity: DuniterIdentity = Depends(get_current_identity), ) -> OrgMemberOut: """Join a transparent organization.""" org = await get_organization(db, org_id) if not org: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Organisation introuvable") if not org.is_transparent: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Rejoindre cette organisation nécessite une invitation", ) if await is_member(db, org_id, identity.id): raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Déjà membre") member = await add_member(db, org_id, identity.id) return OrgMemberOut.model_validate(member)