using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using DramaLing.Api.Data; using DramaLing.Api.Models.Entities; using Microsoft.AspNetCore.Authorization; using System.Security.Claims; namespace DramaLing.Api.Controllers; [ApiController] [Route("api/flashcards-simple")] [Authorize] // 恢復認證要求 public class SimplifiedFlashcardsController : ControllerBase { private readonly DramaLingDbContext _context; private readonly ILogger _logger; public SimplifiedFlashcardsController(DramaLingDbContext context, ILogger logger) { _context = context; _logger = logger; } private Guid GetUserId() { var userIdString = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? User.FindFirst("sub")?.Value; if (Guid.TryParse(userIdString, out var userId)) return userId; // 如果解析失敗,記錄錯誤並拋出異常 _logger.LogError("Failed to extract valid user ID from token. Claims: {Claims}", string.Join(", ", User.Claims.Select(c => $"{c.Type}={c.Value}"))); throw new UnauthorizedAccessException("Invalid user ID in token"); } [HttpGet] public async Task GetFlashcards( [FromQuery] string? search = null, [FromQuery] bool favoritesOnly = false) { try { var userId = GetUserId(); var query = _context.Flashcards .Where(f => f.UserId == userId && !f.IsArchived) .AsQueryable(); // 搜尋篩選 if (!string.IsNullOrEmpty(search)) { query = query.Where(f => f.Word.Contains(search) || f.Translation.Contains(search) || (f.Definition != null && f.Definition.Contains(search))); } // 收藏篩選 if (favoritesOnly) { query = query.Where(f => f.IsFavorite); } var flashcards = await query .OrderByDescending(f => f.CreatedAt) .Select(f => new { f.Id, f.Word, f.Translation, f.Definition, f.PartOfSpeech, f.Pronunciation, f.Example, f.ExampleTranslation, f.MasteryLevel, f.TimesReviewed, f.IsFavorite, f.NextReviewDate, f.DifficultyLevel, f.CreatedAt, f.UpdatedAt // 移除 CardSet 屬性 }) .ToListAsync(); return Ok(new { Success = true, Data = new { Flashcards = flashcards, Count = flashcards.Count } }); } catch (Exception ex) { _logger.LogError(ex, "Error getting flashcards for user"); return StatusCode(500, new { Success = false, Error = "Failed to load flashcards" }); } } [HttpPost] public async Task CreateFlashcard([FromBody] CreateSimpleFlashcardRequest request) { try { var userId = GetUserId(); // 檢測重複詞卡 var existing = await _context.Flashcards .FirstOrDefaultAsync(f => f.UserId == userId && f.Word.ToLower() == request.Word.ToLower() && !f.IsArchived); if (existing != null) { return Ok(new { Success = false, Error = "詞卡已存在", IsDuplicate = true, ExistingCard = new { existing.Id, existing.Word, existing.Translation, existing.CreatedAt } }); } var flashcard = new Flashcard { Id = Guid.NewGuid(), UserId = userId, // 移除 CardSetId,設為 Guid.Empty 或 null CardSetId = Guid.Empty, Word = request.Word, Translation = request.Translation, Definition = request.Definition ?? "", PartOfSpeech = request.PartOfSpeech, Pronunciation = request.Pronunciation, Example = request.Example, ExampleTranslation = request.ExampleTranslation, MasteryLevel = 0, TimesReviewed = 0, IsFavorite = false, NextReviewDate = DateTime.Today, DifficultyLevel = "A2", // 預設等級 EasinessFactor = 2.5f, IntervalDays = 1, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }; _context.Flashcards.Add(flashcard); await _context.SaveChangesAsync(); return Ok(new { Success = true, Data = new { flashcard.Id, flashcard.Word, flashcard.Translation, flashcard.Definition, flashcard.CreatedAt }, Message = "詞卡創建成功" }); } catch (Exception ex) { _logger.LogError(ex, "Error creating flashcard"); return StatusCode(500, new { Success = false, Error = "Failed to create flashcard" }); } } [HttpDelete("{id}")] public async Task DeleteFlashcard(Guid id) { try { var userId = GetUserId(); var flashcard = await _context.Flashcards .FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId); if (flashcard == null) { return NotFound(new { Success = false, Error = "Flashcard not found" }); } _context.Flashcards.Remove(flashcard); await _context.SaveChangesAsync(); return Ok(new { Success = true, Message = "詞卡已刪除" }); } catch (Exception ex) { _logger.LogError(ex, "Error deleting flashcard {FlashcardId}", id); return StatusCode(500, new { Success = false, Error = "Failed to delete flashcard" }); } } [HttpPost("{id}/favorite")] public async Task ToggleFavorite(Guid id) { try { var userId = GetUserId(); var flashcard = await _context.Flashcards .FirstOrDefaultAsync(f => f.Id == id && f.UserId == userId); if (flashcard == null) { return NotFound(new { Success = false, Error = "Flashcard not found" }); } flashcard.IsFavorite = !flashcard.IsFavorite; flashcard.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); return Ok(new { Success = true, IsFavorite = flashcard.IsFavorite, Message = flashcard.IsFavorite ? "已加入收藏" : "已取消收藏" }); } catch (Exception ex) { _logger.LogError(ex, "Error toggling favorite for flashcard {FlashcardId}", id); return StatusCode(500, new { Success = false, Error = "Failed to toggle favorite" }); } } } // 簡化的請求 DTO,移除 CardSetId public class CreateSimpleFlashcardRequest { public string Word { get; set; } = string.Empty; public string Translation { get; set; } = string.Empty; public string Definition { get; set; } = string.Empty; public string PartOfSpeech { get; set; } = string.Empty; public string Pronunciation { get; set; } = string.Empty; public string Example { get; set; } = string.Empty; public string? ExampleTranslation { get; set; } }