using Microsoft.EntityFrameworkCore;
using DramaLing.Api.Models.Entities;
using System.Text.Json;

namespace DramaLing.Api.Data;

public class DramaLingDbContext : DbContext
{
    public DramaLingDbContext(DbContextOptions<DramaLingDbContext> options) : base(options)
    {
    }

    // DbSets
    public DbSet<User> Users { get; set; }
    public DbSet<UserSettings> UserSettings { get; set; }
    public DbSet<CardSet> CardSets { get; set; }
    public DbSet<Flashcard> Flashcards { get; set; }
    public DbSet<Tag> Tags { get; set; }
    public DbSet<FlashcardTag> FlashcardTags { get; set; }
    public DbSet<StudySession> StudySessions { get; set; }
    public DbSet<StudyRecord> StudyRecords { get; set; }
    public DbSet<ErrorReport> ErrorReports { get; set; }
    public DbSet<DailyStats> DailyStats { get; set; }
    public DbSet<SentenceAnalysisCache> SentenceAnalysisCache { get; set; }
    public DbSet<WordQueryUsageStats> WordQueryUsageStats { get; set; }
    public DbSet<AudioCache> AudioCaches { get; set; }
    public DbSet<PronunciationAssessment> PronunciationAssessments { get; set; }
    public DbSet<UserAudioPreferences> UserAudioPreferences { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // 設定表名稱 (與 Supabase 一致)
        modelBuilder.Entity<User>().ToTable("user_profiles");
        modelBuilder.Entity<UserSettings>().ToTable("user_settings");
        modelBuilder.Entity<CardSet>().ToTable("card_sets");
        modelBuilder.Entity<Flashcard>().ToTable("flashcards");
        modelBuilder.Entity<Tag>().ToTable("tags");
        modelBuilder.Entity<FlashcardTag>().ToTable("flashcard_tags");
        modelBuilder.Entity<StudySession>().ToTable("study_sessions");
        modelBuilder.Entity<StudyRecord>().ToTable("study_records");
        modelBuilder.Entity<ErrorReport>().ToTable("error_reports");
        modelBuilder.Entity<DailyStats>().ToTable("daily_stats");
        modelBuilder.Entity<AudioCache>().ToTable("audio_cache");
        modelBuilder.Entity<PronunciationAssessment>().ToTable("pronunciation_assessments");
        modelBuilder.Entity<UserAudioPreferences>().ToTable("user_audio_preferences");

        // 配置屬性名稱 (snake_case)
        ConfigureUserEntity(modelBuilder);
        ConfigureFlashcardEntity(modelBuilder);
        ConfigureStudyEntities(modelBuilder);
        ConfigureTagEntities(modelBuilder);
        ConfigureErrorReportEntity(modelBuilder);
        ConfigureDailyStatsEntity(modelBuilder);
        ConfigureAudioEntities(modelBuilder);

        // 複合主鍵
        modelBuilder.Entity<FlashcardTag>()
            .HasKey(ft => new { ft.FlashcardId, ft.TagId });

        modelBuilder.Entity<DailyStats>()
            .HasIndex(ds => new { ds.UserId, ds.Date })
            .IsUnique();

        // 外鍵關係
        ConfigureRelationships(modelBuilder);
    }

    private void ConfigureUserEntity(ModelBuilder modelBuilder)
    {
        var userEntity = modelBuilder.Entity<User>();
        userEntity.Property(u => u.Username).HasColumnName("username");
        userEntity.Property(u => u.Email).HasColumnName("email");
        userEntity.Property(u => u.PasswordHash).HasColumnName("password_hash");
        userEntity.Property(u => u.DisplayName).HasColumnName("display_name");
        userEntity.Property(u => u.AvatarUrl).HasColumnName("avatar_url");
        userEntity.Property(u => u.SubscriptionType).HasColumnName("subscription_type");
        userEntity.Property(u => u.Preferences)
            .HasColumnName("preferences")
            .HasConversion(
                v => System.Text.Json.JsonSerializer.Serialize(v, (System.Text.Json.JsonSerializerOptions)null),
                v => System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(v, (System.Text.Json.JsonSerializerOptions)null) ?? new Dictionary<string, object>());

        // 新增個人化欄位映射
        userEntity.Property(u => u.EnglishLevel).HasColumnName("english_level");
        userEntity.Property(u => u.LevelUpdatedAt).HasColumnName("level_updated_at");
        userEntity.Property(u => u.IsLevelVerified).HasColumnName("is_level_verified");
        userEntity.Property(u => u.LevelNotes).HasColumnName("level_notes");

        userEntity.Property(u => u.CreatedAt).HasColumnName("created_at");
        userEntity.Property(u => u.UpdatedAt).HasColumnName("updated_at");

        // Add unique indexes
        userEntity.HasIndex(u => u.Email).IsUnique();
        userEntity.HasIndex(u => u.Username).IsUnique();
    }

    private void ConfigureFlashcardEntity(ModelBuilder modelBuilder)
    {
        var flashcardEntity = modelBuilder.Entity<Flashcard>();
        flashcardEntity.Property(f => f.UserId).HasColumnName("user_id");
        flashcardEntity.Property(f => f.CardSetId).HasColumnName("card_set_id");
        flashcardEntity.Property(f => f.PartOfSpeech).HasColumnName("part_of_speech");
        flashcardEntity.Property(f => f.ExampleTranslation).HasColumnName("example_translation");
        flashcardEntity.Property(f => f.EasinessFactor).HasColumnName("easiness_factor");
        flashcardEntity.Property(f => f.IntervalDays).HasColumnName("interval_days");
        flashcardEntity.Property(f => f.NextReviewDate).HasColumnName("next_review_date");
        flashcardEntity.Property(f => f.MasteryLevel).HasColumnName("mastery_level");
        flashcardEntity.Property(f => f.TimesReviewed).HasColumnName("times_reviewed");
        flashcardEntity.Property(f => f.TimesCorrect).HasColumnName("times_correct");
        flashcardEntity.Property(f => f.LastReviewedAt).HasColumnName("last_reviewed_at");
        flashcardEntity.Property(f => f.IsFavorite).HasColumnName("is_favorite");
        flashcardEntity.Property(f => f.IsArchived).HasColumnName("is_archived");
        flashcardEntity.Property(f => f.DifficultyLevel).HasColumnName("difficulty_level");
        flashcardEntity.Property(f => f.CreatedAt).HasColumnName("created_at");
        flashcardEntity.Property(f => f.UpdatedAt).HasColumnName("updated_at");
    }

    private void ConfigureStudyEntities(ModelBuilder modelBuilder)
    {
        var sessionEntity = modelBuilder.Entity<StudySession>();
        sessionEntity.Property(s => s.UserId).HasColumnName("user_id");
        sessionEntity.Property(s => s.SessionType).HasColumnName("session_type");
        sessionEntity.Property(s => s.StartedAt).HasColumnName("started_at");
        sessionEntity.Property(s => s.EndedAt).HasColumnName("ended_at");
        sessionEntity.Property(s => s.TotalCards).HasColumnName("total_cards");
        sessionEntity.Property(s => s.CorrectCount).HasColumnName("correct_count");
        sessionEntity.Property(s => s.DurationSeconds).HasColumnName("duration_seconds");
        sessionEntity.Property(s => s.AverageResponseTimeMs).HasColumnName("average_response_time_ms");

        var recordEntity = modelBuilder.Entity<StudyRecord>();
        recordEntity.Property(r => r.UserId).HasColumnName("user_id");
        recordEntity.Property(r => r.FlashcardId).HasColumnName("flashcard_id");
        recordEntity.Property(r => r.SessionId).HasColumnName("session_id");
        recordEntity.Property(r => r.StudyMode).HasColumnName("study_mode");
        recordEntity.Property(r => r.QualityRating).HasColumnName("quality_rating");
        recordEntity.Property(r => r.ResponseTimeMs).HasColumnName("response_time_ms");
        recordEntity.Property(r => r.UserAnswer).HasColumnName("user_answer");
        recordEntity.Property(r => r.IsCorrect).HasColumnName("is_correct");
        recordEntity.Property(r => r.StudiedAt).HasColumnName("studied_at");
    }

    private void ConfigureTagEntities(ModelBuilder modelBuilder)
    {
        var tagEntity = modelBuilder.Entity<Tag>();
        tagEntity.Property(t => t.UserId).HasColumnName("user_id");
        tagEntity.Property(t => t.UsageCount).HasColumnName("usage_count");
        tagEntity.Property(t => t.CreatedAt).HasColumnName("created_at");

        var flashcardTagEntity = modelBuilder.Entity<FlashcardTag>();
        flashcardTagEntity.Property(ft => ft.FlashcardId).HasColumnName("flashcard_id");
        flashcardTagEntity.Property(ft => ft.TagId).HasColumnName("tag_id");
    }

    private void ConfigureErrorReportEntity(ModelBuilder modelBuilder)
    {
        var errorEntity = modelBuilder.Entity<ErrorReport>();
        errorEntity.Property(e => e.UserId).HasColumnName("user_id");
        errorEntity.Property(e => e.FlashcardId).HasColumnName("flashcard_id");
        errorEntity.Property(e => e.ReportType).HasColumnName("report_type");
        errorEntity.Property(e => e.StudyMode).HasColumnName("study_mode");
        errorEntity.Property(e => e.AdminNotes).HasColumnName("admin_notes");
        errorEntity.Property(e => e.ResolvedAt).HasColumnName("resolved_at");
        errorEntity.Property(e => e.ResolvedBy).HasColumnName("resolved_by");
        errorEntity.Property(e => e.CreatedAt).HasColumnName("created_at");
    }

    private void ConfigureDailyStatsEntity(ModelBuilder modelBuilder)
    {
        var statsEntity = modelBuilder.Entity<DailyStats>();
        statsEntity.Property(d => d.UserId).HasColumnName("user_id");
        statsEntity.Property(d => d.WordsStudied).HasColumnName("words_studied");
        statsEntity.Property(d => d.WordsCorrect).HasColumnName("words_correct");
        statsEntity.Property(d => d.StudyTimeSeconds).HasColumnName("study_time_seconds");
        statsEntity.Property(d => d.SessionCount).HasColumnName("session_count");
        statsEntity.Property(d => d.CardsGenerated).HasColumnName("cards_generated");
        statsEntity.Property(d => d.AiApiCalls).HasColumnName("ai_api_calls");
        statsEntity.Property(d => d.CreatedAt).HasColumnName("created_at");
    }

    private void ConfigureRelationships(ModelBuilder modelBuilder)
    {
        // User relationships
        modelBuilder.Entity<CardSet>()
            .HasOne(cs => cs.User)
            .WithMany(u => u.CardSets)
            .HasForeignKey(cs => cs.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Flashcard>()
            .HasOne(f => f.User)
            .WithMany(u => u.Flashcards)
            .HasForeignKey(f => f.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Flashcard>()
            .HasOne(f => f.CardSet)
            .WithMany(cs => cs.Flashcards)
            .HasForeignKey(f => f.CardSetId)
            .OnDelete(DeleteBehavior.Cascade);

        // Study relationships
        modelBuilder.Entity<StudySession>()
            .HasOne(ss => ss.User)
            .WithMany(u => u.StudySessions)
            .HasForeignKey(ss => ss.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<StudyRecord>()
            .HasOne(sr => sr.Flashcard)
            .WithMany(f => f.StudyRecords)
            .HasForeignKey(sr => sr.FlashcardId)
            .OnDelete(DeleteBehavior.Cascade);

        // Tag relationships
        modelBuilder.Entity<FlashcardTag>()
            .HasOne(ft => ft.Flashcard)
            .WithMany(f => f.FlashcardTags)
            .HasForeignKey(ft => ft.FlashcardId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<FlashcardTag>()
            .HasOne(ft => ft.Tag)
            .WithMany(t => t.FlashcardTags)
            .HasForeignKey(ft => ft.TagId)
            .OnDelete(DeleteBehavior.Cascade);

        // Error report relationships
        modelBuilder.Entity<ErrorReport>()
            .HasOne(er => er.User)
            .WithMany(u => u.ErrorReports)
            .HasForeignKey(er => er.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<ErrorReport>()
            .HasOne(er => er.Flashcard)
            .WithMany(f => f.ErrorReports)
            .HasForeignKey(er => er.FlashcardId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<ErrorReport>()
            .HasOne(er => er.ResolvedByUser)
            .WithMany()
            .HasForeignKey(er => er.ResolvedBy)
            .OnDelete(DeleteBehavior.SetNull);

        // User settings relationship
        modelBuilder.Entity<UserSettings>()
            .HasOne(us => us.User)
            .WithOne(u => u.Settings)
            .HasForeignKey<UserSettings>(us => us.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        // Daily stats relationship
        modelBuilder.Entity<DailyStats>()
            .HasOne(ds => ds.User)
            .WithMany(u => u.DailyStats)
            .HasForeignKey(ds => ds.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        // Sentence analysis cache configuration
        modelBuilder.Entity<SentenceAnalysisCache>()
            .HasIndex(sac => sac.InputTextHash)
            .HasDatabaseName("IX_SentenceAnalysisCache_Hash");

        modelBuilder.Entity<SentenceAnalysisCache>()
            .HasIndex(sac => sac.ExpiresAt)
            .HasDatabaseName("IX_SentenceAnalysisCache_Expires");

        modelBuilder.Entity<SentenceAnalysisCache>()
            .HasIndex(sac => new { sac.InputTextHash, sac.ExpiresAt })
            .HasDatabaseName("IX_SentenceAnalysisCache_Hash_Expires");

        // Word query usage stats configuration
        modelBuilder.Entity<WordQueryUsageStats>()
            .HasOne(wq => wq.User)
            .WithMany()
            .HasForeignKey(wq => wq.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<WordQueryUsageStats>()
            .HasIndex(wq => new { wq.UserId, wq.Date })
            .IsUnique()
            .HasDatabaseName("IX_WordQueryUsageStats_UserDate");

        modelBuilder.Entity<WordQueryUsageStats>()
            .HasIndex(wq => wq.CreatedAt)
            .HasDatabaseName("IX_WordQueryUsageStats_CreatedAt");

        // Audio entities relationships
        ConfigureAudioRelationships(modelBuilder);
    }

    private void ConfigureAudioEntities(ModelBuilder modelBuilder)
    {
        // AudioCache configuration
        var audioCacheEntity = modelBuilder.Entity<AudioCache>();
        audioCacheEntity.Property(ac => ac.TextHash).HasColumnName("text_hash");
        audioCacheEntity.Property(ac => ac.TextContent).HasColumnName("text_content");
        audioCacheEntity.Property(ac => ac.VoiceId).HasColumnName("voice_id");
        audioCacheEntity.Property(ac => ac.AudioUrl).HasColumnName("audio_url");
        audioCacheEntity.Property(ac => ac.FileSize).HasColumnName("file_size");
        audioCacheEntity.Property(ac => ac.DurationMs).HasColumnName("duration_ms");
        audioCacheEntity.Property(ac => ac.CreatedAt).HasColumnName("created_at");
        audioCacheEntity.Property(ac => ac.LastAccessed).HasColumnName("last_accessed");
        audioCacheEntity.Property(ac => ac.AccessCount).HasColumnName("access_count");

        audioCacheEntity.HasIndex(ac => ac.TextHash)
            .IsUnique()
            .HasDatabaseName("IX_AudioCache_TextHash");

        audioCacheEntity.HasIndex(ac => ac.LastAccessed)
            .HasDatabaseName("IX_AudioCache_LastAccessed");

        // PronunciationAssessment configuration
        var pronunciationEntity = modelBuilder.Entity<PronunciationAssessment>();
        pronunciationEntity.Property(pa => pa.UserId).HasColumnName("user_id");
        pronunciationEntity.Property(pa => pa.FlashcardId).HasColumnName("flashcard_id");
        pronunciationEntity.Property(pa => pa.TargetText).HasColumnName("target_text");
        pronunciationEntity.Property(pa => pa.AudioUrl).HasColumnName("audio_url");
        pronunciationEntity.Property(pa => pa.OverallScore).HasColumnName("overall_score");
        pronunciationEntity.Property(pa => pa.AccuracyScore).HasColumnName("accuracy_score");
        pronunciationEntity.Property(pa => pa.FluencyScore).HasColumnName("fluency_score");
        pronunciationEntity.Property(pa => pa.CompletenessScore).HasColumnName("completeness_score");
        pronunciationEntity.Property(pa => pa.ProsodyScore).HasColumnName("prosody_score");
        pronunciationEntity.Property(pa => pa.PhonemeScores).HasColumnName("phoneme_scores");
        pronunciationEntity.Property(pa => pa.Suggestions).HasColumnName("suggestions");
        pronunciationEntity.Property(pa => pa.StudySessionId).HasColumnName("study_session_id");
        pronunciationEntity.Property(pa => pa.PracticeMode).HasColumnName("practice_mode");
        pronunciationEntity.Property(pa => pa.CreatedAt).HasColumnName("created_at");

        pronunciationEntity.HasIndex(pa => new { pa.UserId, pa.FlashcardId })
            .HasDatabaseName("IX_PronunciationAssessment_UserFlashcard");

        pronunciationEntity.HasIndex(pa => pa.StudySessionId)
            .HasDatabaseName("IX_PronunciationAssessment_Session");

        // UserAudioPreferences configuration
        var audioPrefsEntity = modelBuilder.Entity<UserAudioPreferences>();
        audioPrefsEntity.Property(uap => uap.PreferredAccent).HasColumnName("preferred_accent");
        audioPrefsEntity.Property(uap => uap.PreferredVoiceMale).HasColumnName("preferred_voice_male");
        audioPrefsEntity.Property(uap => uap.PreferredVoiceFemale).HasColumnName("preferred_voice_female");
        audioPrefsEntity.Property(uap => uap.DefaultSpeed).HasColumnName("default_speed");
        audioPrefsEntity.Property(uap => uap.AutoPlayEnabled).HasColumnName("auto_play_enabled");
        audioPrefsEntity.Property(uap => uap.PronunciationDifficulty).HasColumnName("pronunciation_difficulty");
        audioPrefsEntity.Property(uap => uap.TargetScoreThreshold).HasColumnName("target_score_threshold");
        audioPrefsEntity.Property(uap => uap.EnableDetailedFeedback).HasColumnName("enable_detailed_feedback");
        audioPrefsEntity.Property(uap => uap.UpdatedAt).HasColumnName("updated_at");
    }

    private void ConfigureAudioRelationships(ModelBuilder modelBuilder)
    {
        // PronunciationAssessment relationships
        modelBuilder.Entity<PronunciationAssessment>()
            .HasOne(pa => pa.User)
            .WithMany()
            .HasForeignKey(pa => pa.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<PronunciationAssessment>()
            .HasOne(pa => pa.Flashcard)
            .WithMany()
            .HasForeignKey(pa => pa.FlashcardId)
            .OnDelete(DeleteBehavior.SetNull);

        modelBuilder.Entity<PronunciationAssessment>()
            .HasOne(pa => pa.StudySession)
            .WithMany()
            .HasForeignKey(pa => pa.StudySessionId)
            .OnDelete(DeleteBehavior.SetNull);

        // UserAudioPreferences relationship
        modelBuilder.Entity<UserAudioPreferences>()
            .HasOne(uap => uap.User)
            .WithOne()
            .HasForeignKey<UserAudioPreferences>(uap => uap.UserId)
            .OnDelete(DeleteBehavior.Cascade);
    }
}