using API.Data; using API.Models; using API.Services; using Microsoft.EntityFrameworkCore; using Model; namespace API.GraphQL; public class Mutation { private static string EnsureAuthenticated(IHttpContextAccessor? httpContextAccessor, ITokenService tokenService) { var httpContext = httpContextAccessor?.HttpContext; if (httpContext == null) throw new Exception("Authentication required. Please provide a valid authorization token."); var authHeader = httpContext.Request.Headers["Authorization"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(authHeader)) throw new Exception("Authentication required. Please login to access this resource."); var token = authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase) ? authHeader["Bearer ".Length..].Trim() : authHeader.Trim(); var username = tokenService.ValidateToken(token); if (string.IsNullOrEmpty(username)) throw new Exception("Invalid or expired authentication token. Please login again."); return username; } public async Task Login( [Service] AppDbContext db, [Service] ITokenService tokenService, string username, string password) { if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) return new LoginPayload { Success = false, Message = "Username and password are required." }; try { var user = await db.Users.FirstOrDefaultAsync(u => u.Username == username); if (user == null) { user = new User { Username = username, Password = password, CreatedAt = DateTime.UtcNow }; db.Users.Add(user); await db.SaveChangesAsync(); } else if (user.Password != password) { return new LoginPayload { Success = false, Message = "Invalid credentials." }; } var token = tokenService.GenerateToken(username); return new LoginPayload { Success = true, Token = token, Username = username, Message = "Login successful." }; } catch (Exception ex) { return new LoginPayload { Success = false, Message = $"Login failed: {ex.Message}" }; } } public async Task SaveNote( [Service] AppDbContext db, [Service] IHttpContextAccessor httpContextAccessor, [Service] ITokenService tokenService, string cardName, string note) { var username = EnsureAuthenticated(httpContextAccessor, tokenService); try { var existing = await db.CardNotes.FirstOrDefaultAsync(n => n.CardName == cardName && n.Username == username); if (existing == null) { var cardNote = new CardNote { Username = username, CardName = cardName, Note = note, UpdatedAt = DateTime.UtcNow }; db.CardNotes.Add(cardNote); await db.SaveChangesAsync(); return cardNote; } existing.Note = note; existing.UpdatedAt = DateTime.UtcNow; await db.SaveChangesAsync(); return existing; } catch (Exception ex) { Console.WriteLine($"[WARNING] Could not save note: {ex.Message}"); return new CardNote { Username = username, CardName = cardName, Note = note }; } } public async Task DeleteNote( [Service] AppDbContext db, [Service] IHttpContextAccessor httpContextAccessor, [Service] ITokenService tokenService, string cardName) { var username = EnsureAuthenticated(httpContextAccessor, tokenService); try { var existing = await db.CardNotes.FirstOrDefaultAsync(n => n.CardName == cardName && n.Username == username); if (existing != null) { db.CardNotes.Remove(existing); await db.SaveChangesAsync(); return true; } return false; } catch (Exception ex) { Console.WriteLine($"[WARNING] Could not delete note: {ex.Message}"); return false; } } public async Task SaveDeck( [Service] AppDbContext db, [Service] IHttpContextAccessor httpContextAccessor, [Service] ITokenService tokenService, UserDeckInput input) { EnsureAuthenticated(httpContextAccessor, tokenService); var id = input.Id ?? Guid.NewGuid(); var existing = await db.UserDecks.FindAsync(id); if (existing == null) { var deck = new UserDeck { Id = id, Name = input.Name, Cards = input.Cards ?? [], Divers = input.Divers ?? [], Notes = input.Notes, Season = input.Season, UpdatedAt = DateTime.UtcNow }; db.UserDecks.Add(deck); await db.SaveChangesAsync(); return deck; } existing.Name = input.Name; existing.Cards = input.Cards ?? []; existing.Divers = input.Divers ?? []; existing.Notes = input.Notes; existing.Season = input.Season; existing.UpdatedAt = DateTime.UtcNow; await db.SaveChangesAsync(); return existing; } public async Task DeleteDeck( [Service] AppDbContext db, [Service] IHttpContextAccessor httpContextAccessor, [Service] ITokenService tokenService, Guid id) { EnsureAuthenticated(httpContextAccessor, tokenService); var existing = await db.UserDecks.FindAsync(id); if (existing != null) { db.UserDecks.Remove(existing); await db.SaveChangesAsync(); return true; } return false; } } public record UserDeckInput( Guid? Id, string Name, List? Cards, List? Divers, string? Notes, string? Season);