using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Tiku.Domain.Catalog;
using Tiku.Domain.Content;
using Tiku.Domain.Identity;
using Tiku.Domain.Tenancy;

namespace Tiku.Infrastructure.Persistence.Configurations;

internal sealed class VocabularyUnitConfiguration : IEntityTypeConfiguration<VocabularyUnit>
{
    public void Configure(EntityTypeBuilder<VocabularyUnit> builder)
    {
        builder.ConfigureTenantEntity("vocabulary_units");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Name).HasMaxLength(300);
        builder.Property(entity => entity.SourceHash).HasMaxLength(128);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.EntryId,
            entity.ContentNodeId,
            entity.RegionId,
            entity.IsActive,
            entity.SortOrder
        });

        builder.HasOne<Region>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentEntry>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentNode>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class VocabularyWordConfiguration : IEntityTypeConfiguration<VocabularyWord>
{
    public void Configure(EntityTypeBuilder<VocabularyWord> builder)
    {
        builder.ConfigureTenantEntity("vocabulary_words");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Word).HasMaxLength(300);
        builder.Property(entity => entity.Phonetic).HasMaxLength(300);
        builder.Property(entity => entity.SourceHash).HasMaxLength(128);
        builder.Property(entity => entity.Tags).IsJson("[]");
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.EntryId,
            entity.ContentNodeId,
            entity.UnitId,
            entity.IsActive,
            entity.SortOrder
        });
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.UnitId,
            entity.IsActive,
            entity.SortOrder,
            entity.CreatedAt
        });

        builder.HasOne<VocabularyUnit>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.UnitId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentEntry>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentNode>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class UserWordProgressConfiguration : IEntityTypeConfiguration<UserWordProgress>
{
    public void Configure(EntityTypeBuilder<UserWordProgress> builder)
    {
        builder.ConfigureTenantEntity("user_word_progress");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.EaseFactor).HasPrecision(4, 2).HasDefaultValue(2.50m);
        builder.Property(entity => entity.LastResult).HasNullableSnakeCaseEnum();
        builder.Property(entity => entity.DueLevel).HasSnakeCaseEnum();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.WordId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.UserId,
            entity.NextReviewAt,
            entity.Status,
            entity.DueLevel
        });

        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_user_word_progress_review_count", "review_count >= 0");
            table.HasCheckConstraint("ck_user_word_progress_correct_streak", "correct_streak >= 0");
            table.HasCheckConstraint("ck_user_word_progress_ease_factor", "ease_factor >= 1.30 and ease_factor <= 3.00");
        });

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<VocabularyWord>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.WordId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class UserWordFavoriteConfiguration : IEntityTypeConfiguration<UserWordFavorite>
{
    public void Configure(EntityTypeBuilder<UserWordFavorite> builder)
    {
        builder.ConfigureTenantEntity("user_word_favorites");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Note).HasMaxLength(1000);
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.WordId }).IsUnique();

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<VocabularyWord>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.WordId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class HandbookSubjectConfiguration : IEntityTypeConfiguration<HandbookSubject>
{
    public void Configure(EntityTypeBuilder<HandbookSubject> builder)
    {
        builder.ConfigureTenantEntity("handbook_subjects");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Name).HasMaxLength(300);
        builder.Property(entity => entity.Type).HasNullableSnakeCaseEnum();
        builder.Property(entity => entity.Icon).HasMaxLength(2048);
        builder.Property(entity => entity.Color).HasMaxLength(50);
        builder.Property(entity => entity.MajorLegacyIds).IsJson("[]");
        builder.Property(entity => entity.SourceHash).HasMaxLength(128);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.EntryId,
            entity.ContentNodeId,
            entity.RegionId,
            entity.IsActive,
            entity.SortOrder
        });

        builder.HasOne<Region>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<School>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.SchoolId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<Major>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.MajorId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentEntry>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentNode>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class HandbookChapterConfiguration : IEntityTypeConfiguration<HandbookChapter>
{
    public void Configure(EntityTypeBuilder<HandbookChapter> builder)
    {
        builder.ConfigureTenantEntity("handbook_chapters");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Name).HasMaxLength(300);
        builder.Property(entity => entity.SourceHash).HasMaxLength(128);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.EntryId,
            entity.ContentNodeId,
            entity.SubjectId,
            entity.IsActive,
            entity.SortOrder
        });

        builder.HasOne<HandbookSubject>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.SubjectId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<ContentEntry>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentNode>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class HandbookEntryConfiguration : IEntityTypeConfiguration<HandbookEntry>
{
    public void Configure(EntityTypeBuilder<HandbookEntry> builder)
    {
        builder.ConfigureTenantEntity("handbook_entries");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Title).HasMaxLength(500);
        builder.Property(entity => entity.Tags).IsJson("[]");
        builder.Property(entity => entity.SourceHash).HasMaxLength(128);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.EntryId,
            entity.ContentNodeId,
            entity.ChapterId,
            entity.IsActive,
            entity.SortOrder
        });

        builder.HasOne<HandbookChapter>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ChapterId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<ContentEntry>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.EntryId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentNode>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ContentNodeId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class QuestionTypeGroupConfiguration : IEntityTypeConfiguration<QuestionTypeGroup>
{
    public void Configure(EntityTypeBuilder<QuestionTypeGroup> builder)
    {
        builder.ConfigureTenantEntity("question_type_groups");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.DisplayName).HasMaxLength(200);
        builder.Property(entity => entity.Types).IsJson("[]");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.SubjectId,
            entity.IsActive,
            entity.SortOrder
        });

        builder.HasOne<Subject>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.SubjectId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class SubjectShareConfiguration : IEntityTypeConfiguration<SubjectShare>
{
    public void Configure(EntityTypeBuilder<SubjectShare> builder)
    {
        builder.ToTable("subject_shares");
        builder.HasKey(entity => new
        {
            entity.TenantId,
            entity.SourceSubjectId,
            entity.TargetSubjectId
        });
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.TargetSubjectId });

        builder.HasOne<Tenant>().WithMany()
            .HasForeignKey(entity => entity.TenantId)
            .OnDelete(DeleteBehavior.Cascade);

        builder.HasOne<Subject>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.SourceSubjectId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<Subject>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.TargetSubjectId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}
