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

namespace Tiku.Infrastructure.Persistence.Configurations;

internal sealed class ContentAssetConfiguration : IEntityTypeConfiguration<ContentAsset>
{
    public void Configure(EntityTypeBuilder<ContentAsset> builder)
    {
        builder.ConfigureTenantEntity("content_assets");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.AssetKey).HasMaxLength(200);
        builder.Property(entity => entity.Title).HasMaxLength(300);
        builder.Property(entity => entity.Category).HasMaxLength(100);
        builder.Property(entity => entity.FileName).HasMaxLength(500);
        builder.Property(entity => entity.CdnUrl).HasMaxLength(2048);
        builder.Property(entity => entity.AssetType).HasSnakeCaseEnum();
        builder.Property(entity => entity.StorageProvider).HasSnakeCaseEnum();
        builder.Property(entity => entity.Bucket).HasMaxLength(200);
        builder.Property(entity => entity.ObjectKey).HasMaxLength(1000);
        builder.Property(entity => entity.MimeType).HasMaxLength(200);
        builder.Property(entity => entity.ChecksumSha256).HasMaxLength(64);
        builder.Property(entity => entity.Visibility).HasSnakeCaseEnum();
        builder.Property(entity => entity.PreviewUrl).HasMaxLength(2048);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.AccessRules).IsJson("{}");
        builder.Property(entity => entity.Source).HasMaxLength(50);
        builder.Property(entity => entity.UploadStatus).HasSnakeCaseEnum();
        builder.Property(entity => entity.VerifiedChecksumSha256).HasMaxLength(64);
        builder.Property(entity => entity.VerificationDetails).IsJson("{}");
        builder.Property(entity => entity.PreviewObjectKey).HasMaxLength(1000);
        builder.Property(entity => entity.PreviewStatus).HasSnakeCaseEnum();
        builder.Property(entity => entity.SecurityScanStatus)
            .HasSnakeCaseEnum()
            .HasDefaultValue(AssetSecurityScanStatus.NotRequired);
        builder.Property(entity => entity.SecurityScanProvider).HasMaxLength(100);
        builder.Property(entity => entity.SecurityScanSummary).IsJson("{}");
        builder.Property(entity => entity.SecurityFlags).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.Visibility,
            entity.AssetType,
            entity.RegionId,
            entity.SubjectId,
            entity.CategoryId,
            entity.SortOrder
        });
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.StorageProvider,
            entity.Bucket,
            entity.ObjectKey
        });
        builder.HasIndex(entity => new { entity.TenantId, entity.ChecksumSha256 })
            .HasFilter("checksum_sha256 is not null");
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.UploadStatus,
            entity.Status,
            entity.UpdatedAt
        });
        builder.HasIndex(entity => new { entity.TenantId, entity.PreviewStatus })
            .HasFilter("preview_status <> 'none'");
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.SecurityScanStatus,
            entity.UploadStatus,
            entity.Status,
            entity.UpdatedAt
        });
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_content_assets_file_size", "file_size_bytes is null or file_size_bytes >= 0");
            table.HasCheckConstraint("ck_content_assets_download_count", "download_count >= 0");
            table.HasCheckConstraint("ck_content_assets_verified_size",
                "verified_size_bytes is null or verified_size_bytes >= 0");
            table.HasCheckConstraint(
                "ck_content_assets_verified_checksum",
                "verified_checksum_sha256 is null or verified_checksum_sha256 ~ '^[a-f0-9]{64}$'");
        });

        builder.HasOne<Region>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<Subject>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.SubjectId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<Category>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.CategoryId })
            .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);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.CreatedBy)
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UpdatedBy)
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.VerifiedBy)
            .OnDelete(DeleteBehavior.SetNull);
    }
}

internal sealed class ContentImportJobConfiguration : IEntityTypeConfiguration<ContentImportJob>
{
    public void Configure(EntityTypeBuilder<ContentImportJob> builder)
    {
        builder.ConfigureTenantEntity("content_import_jobs");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.ImportType).HasSnakeCaseEnum();
        builder.Property(entity => entity.SourceFormat).HasSnakeCaseEnum();
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.SourceName).HasMaxLength(500);
        builder.Property(entity => entity.SourceHash).HasMaxLength(128);
        builder.Property(entity => entity.Summary).IsJson("{}");
        builder.Property(entity => entity.RawPayload).IsJson("[]");
        builder.Property(entity => entity.NormalizedPayload).IsJson("[]");
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.ImportType,
            entity.Status,
            entity.CreatedAt
        });
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_content_import_jobs_counts",
                "total_count >= 0 and valid_count >= 0 and error_count >= 0 and warning_count >= 0 and inserted_count >= 0 and updated_count >= 0 and skipped_count >= 0");
        });

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.CreatedBy)
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<Region>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.TargetRegionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<Subject>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.TargetSubjectId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<Category>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.TargetCategoryId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<ContentNode>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.TargetContentNodeId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
        builder.HasOne<QuestionBank>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.TargetQuestionBankId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Restrict);
    }
}

internal sealed class ContentImportItemConfiguration : IEntityTypeConfiguration<ContentImportItem>
{
    public void Configure(EntityTypeBuilder<ContentImportItem> builder)
    {
        builder.ConfigureTenantEntity("content_import_items");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.ExternalId).HasMaxLength(200);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.TargetType).HasMaxLength(100);
        builder.Property(entity => entity.SourcePayload).IsJson("{}");
        builder.Property(entity => entity.NormalizedPayload).IsJson("{}");
        builder.Property(entity => entity.ContentHash).HasMaxLength(128);
        builder.HasIndex(entity => new { entity.TenantId, entity.JobId, entity.RowNo }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.JobId, entity.Status, entity.RowNo });
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_content_import_items_row_no", "row_no >= 0");
            table.HasCheckConstraint("ck_content_import_items_issues_count", "issues_count >= 0");
        });

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

internal sealed class ContentImportIssueConfiguration : IEntityTypeConfiguration<ContentImportIssue>
{
    public void Configure(EntityTypeBuilder<ContentImportIssue> builder)
    {
        builder.ConfigureTenantEntity("content_import_issues");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Severity).HasSnakeCaseEnum();
        builder.Property(entity => entity.Code).HasMaxLength(100);
        builder.Property(entity => entity.FieldPath).HasMaxLength(500);
        builder.Property(entity => entity.Message).HasMaxLength(2000);
        builder.Property(entity => entity.Details).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.JobId, entity.Severity, entity.RowNo });

        builder.HasOne<ContentImportJob>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.JobId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<ContentImportItem>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ItemId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class ImageAssetConfiguration : IEntityTypeConfiguration<ImageAsset>
{
    public void Configure(EntityTypeBuilder<ImageAsset> builder)
    {
        builder.ConfigureTenantEntity("images");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Title).HasMaxLength(300);
        builder.Property(entity => entity.Category).HasMaxLength(100);
        builder.Property(entity => entity.FileName).HasMaxLength(500);
        builder.Property(entity => entity.CdnUrl).HasMaxLength(2048);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.Category, entity.IsPublic });
    }
}

internal sealed class AppAssetConfiguration : IEntityTypeConfiguration<AppAsset>
{
    public void Configure(EntityTypeBuilder<AppAsset> builder)
    {
        builder.ConfigureTenantEntity("app_assets");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.AssetKey).HasMaxLength(200);
        builder.Property(entity => entity.FileName).HasMaxLength(500);
        builder.Property(entity => entity.Description).HasMaxLength(1000);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.AssetKey }).IsUnique();
    }
}

internal sealed class VideoExplanationConfiguration : IEntityTypeConfiguration<VideoExplanation>
{
    public void Configure(EntityTypeBuilder<VideoExplanation> builder)
    {
        builder.ConfigureTenantEntity("video_explanations");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.LegacySubjectId).HasMaxLength(64);
        builder.Property(entity => entity.Title).HasMaxLength(300);
        builder.Property(entity => entity.VideoUrl).HasMaxLength(2048);
        builder.Property(entity => entity.ThumbnailUrl).HasMaxLength(2048);
        builder.Property(entity => entity.KnowledgeTags).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.SourceHash })
            .HasFilter("source_hash is not null");
        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.Restrict);
    }
}

internal sealed class QuestionVideoConfiguration : IEntityTypeConfiguration<QuestionVideo>
{
    public void Configure(EntityTypeBuilder<QuestionVideo> builder)
    {
        builder.ConfigureTenantEntity("question_videos");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.LegacyQuestionId).HasMaxLength(64);
        builder.Property(entity => entity.LegacyVideoId).HasMaxLength(64);
        builder.Property(entity => entity.VideoType).HasSnakeCaseEnum();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new
        {
            entity.TenantId,
            entity.QuestionId,
            entity.VideoId,
            entity.VideoType
        }).IsUnique();

        builder.HasOne<Question>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.QuestionId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<VideoExplanation>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.VideoId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class VideoPlaybackProgressConfiguration : IEntityTypeConfiguration<VideoPlaybackProgress>
{
    public void Configure(EntityTypeBuilder<VideoPlaybackProgress> builder)
    {
        builder.ConfigureTenantEntity("video_playback_progress");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.Property(entity => entity.LastPlayedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.VideoId })
            .IsUnique()
            .HasFilter("question_id is null");
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.VideoId, entity.QuestionId })
            .IsUnique()
            .HasFilter("question_id is not null");
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.LastPlayedAt });

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

        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_video_playback_progress_position", "position_seconds >= 0");
            table.HasCheckConstraint("ck_video_playback_progress_duration",
                "duration_seconds is null or duration_seconds >= 0");
            table.HasCheckConstraint("ck_video_playback_progress_watched", "watched_seconds >= 0");
            table.HasCheckConstraint("ck_video_playback_progress_play_count", "play_count >= 0");
        });
    }
}