using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Tiku.Domain.Catalog;
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 ExamDateConfiguration : IEntityTypeConfiguration<ExamDate>
{
    public void Configure(EntityTypeBuilder<ExamDate> builder)
    {
        builder.ConfigureTenantEntity("exam_dates");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.ExamName).HasMaxLength(300);
        builder.Property(entity => entity.ExamType).HasMaxLength(50);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.SchoolId, entity.ExamAt });

        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);
    }
}

internal sealed class ReportConfiguration : IEntityTypeConfiguration<Report>
{
    public void Configure(EntityTypeBuilder<Report> builder)
    {
        builder.ConfigureTenantEntity("reports");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Type).HasNullableSnakeCaseEnum();
        builder.Property(entity => entity.Title).HasMaxLength(300);
        builder.Property(entity => entity.Category).HasMaxLength(100);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.Priority).HasSnakeCaseEnum();
        builder.Property(entity => entity.Contact).HasMaxLength(300);
        builder.Property(entity => entity.Attachments).IsJson("[]");
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.CreatedAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.QuestionId, entity.CreatedAt })
            .HasFilter("question_id is not null");

        builder.HasOne<Question>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.HandledBy)
            .OnDelete(DeleteBehavior.SetNull);
    }
}

internal sealed class ReportStatusEventConfiguration : IEntityTypeConfiguration<ReportStatusEvent>
{
    public void Configure(EntityTypeBuilder<ReportStatusEvent> builder)
    {
        builder.ConfigureEntity("report_status_events");
        builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
        builder.Property(entity => entity.FromStatus).HasNullableSnakeCaseEnum();
        builder.Property(entity => entity.ToStatus).HasSnakeCaseEnum();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.ReportId, entity.CreatedAt });

        builder.HasOne<Tenant>().WithMany()
            .HasForeignKey(entity => entity.TenantId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<Report>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ReportId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.ActorUserId)
            .OnDelete(DeleteBehavior.SetNull);
    }
}

internal sealed class UserScoreEventConfiguration : IEntityTypeConfiguration<UserScoreEvent>
{
    public void Configure(EntityTypeBuilder<UserScoreEvent> builder)
    {
        builder.ConfigureEntity("user_score_events");
        builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
        builder.Property(entity => entity.EventType).HasSnakeCaseEnum();
        builder.Property(entity => entity.SourceType).HasMaxLength(100);
        builder.Property(entity => entity.IdempotencyKey).HasMaxLength(200);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.IdempotencyKey }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.CreatedAt });

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

internal sealed class PracticeDailyUsageConfiguration : IEntityTypeConfiguration<PracticeDailyUsage>
{
    public void Configure(EntityTypeBuilder<PracticeDailyUsage> builder)
    {
        builder.ConfigureTenantEntity("practice_daily_usage");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.UsageDate).HasDefaultValueSql("current_date");
        builder.Property(entity => entity.ScopeType).HasSnakeCaseEnum();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.UsageDate });
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.UserId,
            entity.UsageDate,
            entity.ScopeType,
            entity.ScopeId
        }).IsUnique().AreNullsDistinct(false);
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_practice_daily_usage_free_limit", "free_limit >= 0");
            table.HasCheckConstraint("ck_practice_daily_usage_used_count", "used_count >= 0");
        });

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

internal sealed class PracticeAccessEventConfiguration : IEntityTypeConfiguration<PracticeAccessEvent>
{
    public void Configure(EntityTypeBuilder<PracticeAccessEvent> builder)
    {
        builder.ConfigureEntity("practice_access_events");
        builder.HasAlternateKey(entity => new { entity.TenantId, entity.Id });
        builder.Property(entity => entity.EventType).HasSnakeCaseEnum();
        builder.Property(entity => entity.AccessMode).HasSnakeCaseEnum();
        builder.Property(entity => entity.ScopeType).HasNullableSnakeCaseEnum();
        builder.Property(entity => entity.Reason).HasMaxLength(1000);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.CreatedAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.PracticeSessionId });
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_practice_access_events_requested_count", "requested_count >= 0");
            table.HasCheckConstraint("ck_practice_access_events_granted_count", "granted_count >= 0");
            table.HasCheckConstraint("ck_practice_access_events_consumed_free_quota", "consumed_free_quota >= 0");
        });

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

internal sealed class PracticeSessionReportConfiguration : IEntityTypeConfiguration<PracticeSessionReport>
{
    public void Configure(EntityTypeBuilder<PracticeSessionReport> builder)
    {
        builder.ConfigureTenantEntity("practice_session_reports");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Mode).HasMaxLength(50);
        builder.Property(entity => entity.Score).HasPrecision(10, 2);
        builder.Property(entity => entity.TotalScore).HasPrecision(10, 2);
        builder.Property(entity => entity.Accuracy).HasPrecision(6, 4);
        builder.Property(entity => entity.SubmittedAt).HasDefaultValueSql("now()");
        builder.Property(entity => entity.SectionStats).IsJson("[]");
        builder.Property(entity => entity.QuestionResults).IsJson("[]");
        builder.Property(entity => entity.WrongQuestionIds).IsJson("[]");
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.PracticeSessionId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.SubmittedAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.BlueprintId, entity.SubmittedAt })
            .HasFilter("blueprint_id is not null");
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_practice_session_reports_counts", "total_questions >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0");
            table.HasCheckConstraint("ck_practice_session_reports_scores", "score >= 0 and total_score >= 0");
            table.HasCheckConstraint("ck_practice_session_reports_accuracy", "accuracy >= 0 and accuracy <= 1");
            table.HasCheckConstraint("ck_practice_session_reports_duration", "duration_seconds >= 0");
        });

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<PracticeSession>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.PracticeSessionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .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);
    }
}

internal sealed class PracticeSessionReportSectionConfiguration : IEntityTypeConfiguration<PracticeSessionReportSection>
{
    public void Configure(EntityTypeBuilder<PracticeSessionReportSection> builder)
    {
        builder.ConfigureTenantEntity("practice_session_report_sections");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.SectionKey).HasMaxLength(100);
        builder.Property(entity => entity.SectionName).HasMaxLength(300);
        builder.Property(entity => entity.QuestionType).HasMaxLength(50);
        builder.Property(entity => entity.Score).HasPrecision(10, 2);
        builder.Property(entity => entity.TotalScore).HasPrecision(10, 2);
        builder.Property(entity => entity.Accuracy).HasPrecision(6, 4);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.ReportId, entity.SectionKey }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.ReportId, entity.SortOrder });
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_practice_session_report_sections_counts", "question_count >= 0 and answered_count >= 0 and correct_count >= 0 and wrong_count >= 0 and unanswered_count >= 0");
            table.HasCheckConstraint("ck_practice_session_report_sections_scores", "score >= 0 and total_score >= 0");
            table.HasCheckConstraint("ck_practice_session_report_sections_accuracy", "accuracy >= 0 and accuracy <= 1");
        });

        builder.HasOne<PracticeSessionReport>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ReportId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<PracticeSession>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.PracticeSessionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class DashboardDailyStatConfiguration : IEntityTypeConfiguration<DashboardDailyStat>
{
    public void Configure(EntityTypeBuilder<DashboardDailyStat> builder)
    {
        builder.ConfigureTenantEntity("dashboard_daily_stats");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.LegacyRegionId).HasMaxLength(64);
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.StatDate, entity.RegionId }).IsUnique().AreNullsDistinct(false);

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

internal sealed class RevenueDailyStatConfiguration : IEntityTypeConfiguration<RevenueDailyStat>
{
    public void Configure(EntityTypeBuilder<RevenueDailyStat> builder)
    {
        builder.ConfigureTenantEntity("revenue_daily_stats");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.LegacyRegionId).HasMaxLength(64);
        builder.Property(entity => entity.SaleType).HasMaxLength(50);
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.StatDate, entity.RegionId, entity.SaleType }).IsUnique().AreNullsDistinct(false);

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