using System.Security.Claims; using Microsoft.EntityFrameworkCore; using TafsirQuran.Api.Security; using TafsirQuran.Domain.Entities; using TafsirQuran.Infrastructure.Persistence; using TafsirQuran.Shared.Dtos; namespace TafsirQuran.Api.Endpoints; public static class FavoriteEndpoints { public static IEndpointRouteBuilder MapFavoriteEndpoints(this WebApplication app) { var group = app.MapGroup("/api/favorites/ayahs"); group.MapGet("/", async (string? deviceId, ClaimsPrincipal user, AppDbContext db, CancellationToken ct) => { if (!EndpointSecurity.TryResolveOwner(user, deviceId, out var userId, out var deviceKey, out var error)) return error!; var query = db.AyahFavorites.AsNoTracking() .Include(f => f.Surah) .Include(f => f.Tafsir) .AsQueryable(); query = userId.HasValue ? query.Where(f => f.UserId == userId) : query.Where(f => f.DeviceId == deviceKey); var items = await query.OrderByDescending(f => f.CreatedAt).ToListAsync(ct); return Results.Ok(items.Select(f => new AyahFavoriteDto( f.Id, f.TafsirId, f.Tafsir.Slug, f.SurahId, f.Surah.NameFa, f.AyahNumber, f.CreatedAt)).ToList()); }).RequireRateLimiting("read"); group.MapPost("/", async (ToggleAyahRequest request, ClaimsPrincipal user, AppDbContext db, CancellationToken ct) => { if (!EndpointSecurity.TryResolveOwner(user, request.DeviceId, out var userId, out var deviceKey, out var error)) return error!; if (!EndpointSecurity.IsValidSurahAyah(request.SurahNumber, request.AyahNumber)) return Results.BadRequest(new { error = "invalid_ayah" }); var exists = userId.HasValue ? await db.AyahFavorites.AnyAsync(f => f.TafsirId == request.TafsirId && f.SurahId == request.SurahNumber && f.AyahNumber == request.AyahNumber && f.UserId == userId, ct) : await db.AyahFavorites.AnyAsync(f => f.TafsirId == request.TafsirId && f.SurahId == request.SurahNumber && f.AyahNumber == request.AyahNumber && f.DeviceId == deviceKey, ct); if (exists) return Results.NoContent(); db.AyahFavorites.Add(new AyahFavorite { DeviceId = deviceKey ?? request.DeviceId.Trim(), UserId = userId, TafsirId = request.TafsirId, SurahId = request.SurahNumber, AyahNumber = request.AyahNumber }); await db.SaveChangesAsync(ct); return Results.Created($"/api/favorites/ayahs/{request.SurahNumber}/{request.AyahNumber}", null); }).RequireRateLimiting("write"); group.MapDelete("/{surahNumber:int}/{ayahNumber:int}", async ( int surahNumber, int ayahNumber, Guid tafsirId, string? deviceId, ClaimsPrincipal user, AppDbContext db, CancellationToken ct) => { if (!EndpointSecurity.TryResolveOwner(user, deviceId, out var userId, out var deviceKey, out var error)) return error!; if (!EndpointSecurity.IsValidSurahAyah(surahNumber, ayahNumber)) return Results.BadRequest(new { error = "invalid_ayah" }); var items = userId.HasValue ? await db.AyahFavorites .Where(f => f.TafsirId == tafsirId && f.SurahId == surahNumber && f.AyahNumber == ayahNumber && f.UserId == userId) .ToListAsync(ct) : await db.AyahFavorites .Where(f => f.TafsirId == tafsirId && f.SurahId == surahNumber && f.AyahNumber == ayahNumber && f.DeviceId == deviceKey) .ToListAsync(ct); db.AyahFavorites.RemoveRange(items); await db.SaveChangesAsync(ct); return Results.NoContent(); }).RequireRateLimiting("write"); return app; } }