using System.Security.Claims; using Backend.Data; using Backend.Dtos; using Backend.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace Backend.Controllers; [ApiController] [Route("api/todos")] [Authorize] public class TodosController(AppDbContext db) : ControllerBase { private Guid CurrentUserId() { var sub = User.FindFirstValue("sub") ?? throw new InvalidOperationException("Authenticated request missing 'sub' claim"); return Guid.Parse(sub); } [HttpGet] public async Task>> List() { var userId = CurrentUserId(); var todos = await db.Todos .Where(t => t.UserId == userId) .OrderByDescending(t => t.CreatedAt) .Select(t => new TodoDto(t.Id, t.Title, t.Completed, t.CreatedAt)) .ToListAsync(); return Ok(todos); } [HttpPost] public async Task> Create([FromBody] CreateTodoRequest body) { if (!ModelState.IsValid) return ValidationProblem(ModelState); var userId = CurrentUserId(); var todo = new Todo { UserId = userId, Title = body.Title, Completed = false, CreatedAt = DateTimeOffset.UtcNow }; db.Todos.Add(todo); await db.SaveChangesAsync(); var dto = new TodoDto(todo.Id, todo.Title, todo.Completed, todo.CreatedAt); return CreatedAtAction(nameof(List), new { id = todo.Id }, dto); } [HttpPatch("{id:long}")] public async Task> Update(long id, [FromBody] UpdateTodoRequest body) { var userId = CurrentUserId(); var todo = await db.Todos.FirstOrDefaultAsync(t => t.Id == id && t.UserId == userId); if (todo is null) return NotFound(); if (body.Completed.HasValue) todo.Completed = body.Completed.Value; await db.SaveChangesAsync(); return Ok(new TodoDto(todo.Id, todo.Title, todo.Completed, todo.CreatedAt)); } [HttpDelete("{id:long}")] public async Task Delete(long id) { var userId = CurrentUserId(); var affected = await db.Todos .Where(t => t.Id == id && t.UserId == userId) .ExecuteDeleteAsync(); return affected == 0 ? NotFound() : NoContent(); } }