import uuid from datetime import datetime, timedelta, timezone from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import Response from sqlalchemy import select, and_ from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_session from app.models.todos import TodoItem from app.schemas.todos import TodoCreate, TodoUpdate, PostponeRequest, TodoResponse router = APIRouter() @router.get("/", response_model=list[TodoResponse]) async def list_todos( domain: str | None = None, status: str | None = "pending", priority: str | None = None, tag: str | None = None, due_after: datetime | None = None, due_before: datetime | None = None, limit: int = 200, session: AsyncSession = Depends(get_session), ): conditions = [] if domain: conditions.append(TodoItem.domains.contains([domain])) if status: conditions.append(TodoItem.status == status) if priority: conditions.append(TodoItem.priority == priority) if tag: conditions.append(TodoItem.tags.contains([tag])) if due_after: conditions.append(TodoItem.due_date >= due_after) if due_before: conditions.append(TodoItem.due_date <= due_before) stmt = select(TodoItem) if conditions: stmt = stmt.where(and_(*conditions)) stmt = stmt.order_by(TodoItem.due_date.asc().nulls_last(), TodoItem.created_at.desc()) stmt = stmt.limit(limit) result = await session.execute(stmt) return result.scalars().all() @router.post("/", response_model=TodoResponse, status_code=201) async def create_todo( payload: TodoCreate, session: AsyncSession = Depends(get_session), ): item = TodoItem(**payload.model_dump()) session.add(item) await session.commit() await session.refresh(item) return item @router.patch("/{item_id}", response_model=TodoResponse) async def update_todo( item_id: uuid.UUID, payload: TodoUpdate, session: AsyncSession = Depends(get_session), ): item = await session.get(TodoItem, item_id) if not item: raise HTTPException(status_code=404, detail="Tâche introuvable") for field, value in payload.model_dump(exclude_unset=True).items(): setattr(item, field, value) item.updated_at = datetime.now(timezone.utc) await session.commit() await session.refresh(item) return item @router.delete("/{item_id}", status_code=204) async def delete_todo( item_id: uuid.UUID, session: AsyncSession = Depends(get_session), ): item = await session.get(TodoItem, item_id) if not item: raise HTTPException(status_code=404, detail="Tâche introuvable") await session.delete(item) await session.commit() return Response(status_code=204) @router.post("/{item_id}/postpone", response_model=TodoResponse) async def postpone_todo( item_id: uuid.UUID, payload: PostponeRequest, session: AsyncSession = Depends(get_session), ): item = await session.get(TodoItem, item_id) if not item: raise HTTPException(status_code=404, detail="Tâche introuvable") now = datetime.now(timezone.utc) base = item.due_date if item.due_date else now item.due_date = base + timedelta(days=payload.days) item.postponed_count += 1 item.updated_at = now await session.commit() await session.refresh(item) return item