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

namespace Tiku.Infrastructure.Persistence.Configurations;

internal sealed class PracticeSessionConfiguration : IEntityTypeConfiguration<PracticeSession>
{
    public void Configure(EntityTypeBuilder<PracticeSession> builder)
    {
        builder.ConfigureTenantEntity("practice_sessions");
        builder.HasAlternateKey(entity => new { entity.TenantId, entity.UserId, entity.Id });
        builder.Property(entity => entity.Mode).HasMaxLength(50);
        builder.Property(entity => entity.TargetType).HasMaxLength(50);
        builder.Property(entity => entity.StartedAt).HasDefaultValueSql("now()");
        builder.Property(entity => entity.TotalScore).HasPrecision(8, 2);
        builder.Property(entity => entity.AccessMode)
            .HasSnakeCaseEnum()
            .HasDefaultValue(PracticeAccessMode.Free);
        builder.Property(entity => entity.AccessSnapshot).IsJson("{}");
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.StartedAt });

        builder.ToTable(table =>
        {
            table.HasCheckConstraint(
                "ck_practice_sessions_consumed_free_quota",
                "consumed_free_quota >= 0");
        });

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<PracticeBlueprint>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.BlueprintId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<QuestionCollection>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.CollectionId })
            .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 PracticeSessionQuestionConfiguration : IEntityTypeConfiguration<PracticeSessionQuestion>
{
    public void Configure(EntityTypeBuilder<PracticeSessionQuestion> builder)
    {
        builder.ConfigureTenantEntity("practice_session_questions");
        builder.HasAlternateKey(entity => new { entity.TenantId, entity.PracticeSessionId, entity.Id });
        builder.HasIndex(entity => new { entity.TenantId, entity.PracticeSessionId, entity.Position }).IsUnique();
        builder.Property(entity => entity.Score).HasPrecision(8, 2);

        builder.HasOne<PracticeSession>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.PracticeSessionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<TenantQuestionReference>().WithMany()
            .HasForeignKey(entity => new
            {
                entity.TenantId,
                entity.QuestionOwnerTenantId,
                entity.QuestionId
            })
            .HasPrincipalKey(entity => new
            {
                entity.TenantId,
                entity.QuestionOwnerTenantId,
                entity.QuestionId
            })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<QuestionVersion>().WithMany()
            .HasForeignKey(entity => new
            {
                TenantId = entity.QuestionOwnerTenantId,
                entity.QuestionId,
                Id = entity.QuestionVersionId
            })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.QuestionId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class AnswerRecordConfiguration : IEntityTypeConfiguration<AnswerRecord>
{
    public void Configure(EntityTypeBuilder<AnswerRecord> builder)
    {
        builder.ConfigureTenantEntity("answer_records");
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.LegacyQuestionId).HasMaxLength(64);
        builder.Property(entity => entity.LegacyCategoryId).HasMaxLength(64);
        builder.Property(entity => entity.SelectedOptions).IsJson("[]");
        builder.Property(entity => entity.AnsweredAt).HasDefaultValueSql("now()");
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.UserId,
            entity.PracticeSessionId
        });

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<PracticeSession>().WithMany()
            .HasForeignKey(entity => new
            {
                entity.TenantId,
                entity.UserId,
                entity.PracticeSessionId
            })
            .HasPrincipalKey(entity => new
            {
                entity.TenantId,
                entity.UserId,
                entity.Id
            })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<PracticeSessionQuestion>().WithMany()
            .HasForeignKey(entity => new
            {
                entity.TenantId,
                entity.PracticeSessionId,
                Id = entity.SessionQuestionId
            })
            .HasPrincipalKey(entity => new
            {
                entity.TenantId,
                entity.PracticeSessionId,
                entity.Id
            })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class FavoriteQuestionConfiguration : IEntityTypeConfiguration<FavoriteQuestion>
{
    public void Configure(EntityTypeBuilder<FavoriteQuestion> builder)
    {
        builder.ToTable("favorite_questions");
        builder.HasKey(entity => new { entity.TenantId, entity.UserId, entity.QuestionReferenceId });
        builder.Property(entity => entity.Source).HasMaxLength(50);
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");

        builder.HasOne<Tenant>().WithMany()
            .HasForeignKey(entity => entity.TenantId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<TenantQuestionReference>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.QuestionReferenceId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<Question>().WithMany()
            .HasForeignKey(entity => new { entity.QuestionOwnerTenantId, entity.QuestionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class WrongQuestionConfiguration : IEntityTypeConfiguration<WrongQuestion>
{
    public void Configure(EntityTypeBuilder<WrongQuestion> builder)
    {
        builder.ToTable("wrong_questions");
        builder.HasKey(entity => new { entity.TenantId, entity.UserId, entity.QuestionReferenceId });
        builder.Property(entity => entity.WrongCount).HasDefaultValue(1);
        builder.Property(entity => entity.LastWrongAt).HasDefaultValueSql("now()");

        builder.HasOne<Tenant>().WithMany()
            .HasForeignKey(entity => entity.TenantId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<TenantQuestionReference>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.QuestionReferenceId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<Question>().WithMany()
            .HasForeignKey(entity => new { entity.QuestionOwnerTenantId, entity.QuestionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class RecentPracticeConfiguration : IEntityTypeConfiguration<RecentPractice>
{
    public void Configure(EntityTypeBuilder<RecentPractice> builder)
    {
        builder.ConfigureTenantEntity("recent_practices");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.PracticeType).HasMaxLength(50);
        builder.Property(entity => entity.TargetLegacyId).HasMaxLength(64);
        builder.Property(entity => entity.TargetName).HasMaxLength(300);
        builder.Property(entity => entity.Color).HasMaxLength(50);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.LastAccessAt });

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
    }
}
