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

namespace Tiku.Infrastructure.Persistence.Configurations;

internal sealed class TenantExternalProviderConfiguration : IEntityTypeConfiguration<TenantExternalProvider>
{
    public void Configure(EntityTypeBuilder<TenantExternalProvider> builder)
    {
        builder.ConfigureTenantEntity("tenant_external_providers");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Capability).HasSnakeCaseEnum();
        builder.Property(entity => entity.Provider).HasMaxLength(50);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.DisplayName).HasMaxLength(200);
        builder.Property(entity => entity.SecretRef).HasMaxLength(300);
        builder.Property(entity => entity.ConfigPublic).IsJson("{}");
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.Capability, entity.Provider }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.Capability, entity.Status, entity.Priority });
    }
}

internal sealed class TenantSecretConfiguration : IEntityTypeConfiguration<TenantSecret>
{
    public void Configure(EntityTypeBuilder<TenantSecret> builder)
    {
        builder.ConfigureTenantEntity("tenant_secrets");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Purpose).HasMaxLength(80);
        builder.Property(entity => entity.Provider).HasMaxLength(50);
        builder.Property(entity => entity.SecretKey).HasMaxLength(120);
        builder.Property(entity => entity.SecretRef).HasMaxLength(300);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.EncryptionKeyId).HasMaxLength(100);
        builder.HasIndex(entity => new { entity.TenantId, entity.SecretRef }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.Purpose, entity.Provider, entity.SecretKey })
            .IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.Purpose, entity.Provider, entity.Status });
        builder.ToTable(table => table.HasCheckConstraint(
            "ck_tenant_secrets_encryption_envelope",
            "octet_length(encrypted_payload) > 0 and octet_length(encryption_nonce) = 12 and " +
            "octet_length(encryption_tag) = 16 and encryption_key_id <> ''"));
    }
}

internal sealed class SmsVerificationCodeConfiguration : IEntityTypeConfiguration<SmsVerificationCode>
{
    public void Configure(EntityTypeBuilder<SmsVerificationCode> builder)
    {
        builder.ConfigureEntity("sms_verification_codes");
        builder.Property(entity => entity.Phone).HasMaxLength(32);
        builder.Property(entity => entity.Purpose).HasSnakeCaseEnum();
        builder.Property(entity => entity.CodeHash).HasMaxLength(256);
        builder.Property(entity => entity.Provider).HasMaxLength(50);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.IpAddress).HasMaxLength(64);
        builder.Property(entity => entity.UserAgent).HasMaxLength(1000);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.Phone, entity.Purpose, entity.ExpiresAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.Phone, entity.Purpose, entity.CreatedAt })
            .HasFilter("consumed_at is null and status in ('pending', 'sent')");
        builder.HasIndex(entity => new { entity.TenantId, entity.Phone, entity.Purpose })
            .IsUnique()
            .HasFilter("consumed_at is null and status in ('pending', 'sent')");
        builder.ToTable(table => { table.HasCheckConstraint("ck_sms_verification_codes_attempts", "attempts >= 0"); });

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

internal sealed class AuthLoginEventConfiguration : IEntityTypeConfiguration<AuthLoginEvent>
{
    public void Configure(EntityTypeBuilder<AuthLoginEvent> builder)
    {
        builder.ConfigureEntity("auth_login_events");
        builder.Property(entity => entity.Provider).HasMaxLength(50);
        builder.Property(entity => entity.Identifier).HasMaxLength(320);
        builder.Property(entity => entity.Result).HasSnakeCaseEnum();
        builder.Property(entity => entity.FailureCode).HasMaxLength(100);
        builder.Property(entity => entity.IpAddress).HasMaxLength(64);
        builder.Property(entity => entity.UserAgent).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.HasOne<Tenant>().WithMany()
            .HasForeignKey(entity => entity.TenantId)
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UserId)
            .OnDelete(DeleteBehavior.SetNull);
    }
}

internal sealed class AuthSessionConfiguration : IEntityTypeConfiguration<AuthSession>
{
    public void Configure(EntityTypeBuilder<AuthSession> builder)
    {
        builder.ConfigureEntity("auth_sessions");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Realm).HasSnakeCaseEnum();
        builder.Property(entity => entity.TokenHash).HasMaxLength(256);
        builder.Property(entity => entity.SecurityStamp).HasMaxLength(128);
        builder.Property(entity => entity.Provider).HasMaxLength(50);
        builder.Property(entity => entity.RevokedReason).HasMaxLength(100);
        builder.Property(entity => entity.IpAddress).HasMaxLength(64);
        builder.Property(entity => entity.UserAgent).HasMaxLength(1000);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => entity.TokenHash)
            .IsUnique()
            .HasAnnotation("Tiku:GlobalUnique", true);
        builder.HasIndex(entity => new { entity.Realm, entity.TenantId, entity.UserId, entity.ExpiresAt })
            .HasFilter("revoked_at is null");
        builder.HasIndex(entity => new { entity.TokenFamilyId, entity.RevokedAt });

        builder.ToTable(table => table.HasCheckConstraint(
            "ck_auth_sessions_realm_tenant",
            "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)"));

        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 AuthChallengeConfiguration : IEntityTypeConfiguration<AuthChallenge>
{
    public void Configure(EntityTypeBuilder<AuthChallenge> builder)
    {
        builder.ConfigureEntity("auth_challenges");
        builder.Property(entity => entity.Realm).HasSnakeCaseEnum();
        builder.Property(entity => entity.Purpose).HasSnakeCaseEnum();
        builder.Property(entity => entity.TokenHash).HasMaxLength(64);
        builder.Property(entity => entity.SecurityStamp).HasMaxLength(128);
        builder.Property(entity => entity.Provider).HasMaxLength(50);
        builder.Property(entity => entity.IpAddress).HasMaxLength(100);
        builder.Property(entity => entity.UserAgent).HasMaxLength(1024);
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => entity.TokenHash).IsUnique();
        builder.HasIndex(entity => new { entity.UserId, entity.Purpose, entity.ExpiresAt });
        builder.ToTable(table => table.HasCheckConstraint(
            "ck_auth_challenges_realm_tenant",
            "(realm = 'tenant' and tenant_id is not null) or (realm = 'platform' and tenant_id is null)"));
        builder.HasOne<User>().WithMany().HasForeignKey(entity => entity.UserId).OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class SmsSendRateLimitConfiguration : IEntityTypeConfiguration<SmsSendRateLimit>
{
    public void Configure(EntityTypeBuilder<SmsSendRateLimit> builder)
    {
        builder.ToTable("sms_send_rate_limits");
        builder.HasKey(entity => new
        {
            entity.TenantId,
            entity.Dimension,
            entity.ScopeHash,
            entity.BucketStart
        });
        builder.Property(entity => entity.Dimension).HasSnakeCaseEnum();
        builder.Property(entity => entity.ScopeHash).HasMaxLength(128);
        builder.Property(entity => entity.RequestCount).HasDefaultValue(0);
        builder.Property(entity => entity.UpdatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => entity.UpdatedAt);
        builder.ToTable(table =>
        {
            table.HasCheckConstraint("ck_sms_send_rate_limits_request_count", "request_count >= 0");
        });

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

internal sealed class SmsChannelConfiguration : IEntityTypeConfiguration<SmsChannel>
{
    public void Configure(EntityTypeBuilder<SmsChannel> builder)
    {
        builder.ConfigureTenantEntity("sms_channels");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Provider).HasMaxLength(80);
        builder.Property(entity => entity.Name).HasMaxLength(200);
        builder.Property(entity => entity.Signature).HasMaxLength(100);
        builder.Property(entity => entity.Scene).HasMaxLength(100);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.SecretRef).HasMaxLength(300);
        builder.Property(entity => entity.ConfigPublic).IsJson("{}");
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.Provider, entity.Scene }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.Priority });
        builder.ToTable(table => table.HasCheckConstraint("ck_sms_channels_quota", "monthly_quota >= 0"));
    }
}

internal sealed class SmsTemplateConfiguration : IEntityTypeConfiguration<SmsTemplate>
{
    public void Configure(EntityTypeBuilder<SmsTemplate> builder)
    {
        builder.ConfigureTenantEntity("sms_templates");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Code).HasMaxLength(120);
        builder.Property(entity => entity.Name).HasMaxLength(200);
        builder.Property(entity => entity.Type).HasSnakeCaseEnum();
        builder.Property(entity => entity.AuditStatus).HasSnakeCaseEnum();
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.ProviderTemplateCode).HasMaxLength(120);
        builder.Property(entity => entity.Content).HasMaxLength(1000);
        builder.Property(entity => entity.Remark).HasMaxLength(500);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.Code }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.ChannelId, entity.Status });
        builder.HasOne<SmsChannel>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ChannelId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.Cascade);
    }
}

internal sealed class SmsSendLogConfiguration : IEntityTypeConfiguration<SmsSendLog>
{
    public void Configure(EntityTypeBuilder<SmsSendLog> builder)
    {
        builder.ToTable("sms_send_logs");
        builder.HasKey(entity => entity.Id);
        builder.Property(entity => entity.Id).HasDefaultValueSql("gen_random_uuid()");
        builder.Property(entity => entity.Provider).HasMaxLength(80);
        builder.Property(entity => entity.Scene).HasMaxLength(100);
        builder.Property(entity => entity.PhoneMasked).HasMaxLength(40);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.ProviderMessageId).HasMaxLength(200);
        builder.Property(entity => entity.ErrorCode).HasMaxLength(120);
        builder.Property(entity => entity.ErrorMessage).HasMaxLength(1000);
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.Property(entity => entity.CreatedAt).HasDefaultValueSql("now()");
        builder.HasIndex(entity => new { entity.TenantId, entity.CreatedAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.ChannelId, entity.CreatedAt });
        builder.HasOne<Tenant>().WithMany().HasForeignKey(entity => entity.TenantId).OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<SmsChannel>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ChannelId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<SmsTemplate>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.TemplateId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.SetNull);
    }
}

internal sealed class TenantClassConfiguration : IEntityTypeConfiguration<TenantClass>
{
    public void Configure(EntityTypeBuilder<TenantClass> builder)
    {
        builder.ConfigureTenantEntity("tenant_classes");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.LegacyId).HasMaxLength(64);
        builder.Property(entity => entity.Code).HasMaxLength(100);
        builder.Property(entity => entity.Name).HasMaxLength(200);
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.LegacyId }).IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.Code })
            .IsUnique()
            .HasFilter("code is not null and code <> ''");
        builder.HasIndex(entity => new { entity.TenantId, entity.Status, entity.SortOrder, entity.CreatedAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.RegionId })
            .HasFilter("region_id is not null");

        builder.HasOne<Region>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.RegionId })
            .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);
    }
}

internal sealed class TenantClassMemberConfiguration : IEntityTypeConfiguration<TenantClassMember>
{
    public void Configure(EntityTypeBuilder<TenantClassMember> builder)
    {
        builder.ConfigureTenantEntity("tenant_class_members");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.MemberType).HasSnakeCaseEnum();
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.JoinedAt).HasDefaultValueSql("now()");
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.ClassId, entity.UserId, entity.MemberType })
            .IsUnique();
        builder.HasIndex(entity => new { entity.TenantId, entity.ClassId, entity.Status, entity.MemberType });
        builder.HasIndex(entity => new { entity.TenantId, entity.UserId, entity.Status, entity.MemberType });

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

internal sealed class TenantStudentNoteConfiguration : IEntityTypeConfiguration<TenantStudentNote>
{
    public void Configure(EntityTypeBuilder<TenantStudentNote> builder)
    {
        builder.ConfigureTenantEntity("tenant_student_notes");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.NoteType).HasSnakeCaseEnum();
        builder.Property(entity => entity.Visibility).HasSnakeCaseEnum();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new { entity.TenantId, entity.StudentUserId, entity.IsPinned, entity.CreatedAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.CreatedBy, entity.CreatedAt })
            .HasFilter("created_by is not null");

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.StudentUserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.CreatedBy)
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.UpdatedBy)
            .OnDelete(DeleteBehavior.SetNull);
    }
}

internal sealed class TenantStudentFollowupConfiguration : IEntityTypeConfiguration<TenantStudentFollowup>
{
    public void Configure(EntityTypeBuilder<TenantStudentFollowup> builder)
    {
        builder.ConfigureTenantEntity("tenant_student_followups");
        builder.ConfigureTimestamps();
        builder.Property(entity => entity.Title).HasMaxLength(300);
        builder.Property(entity => entity.FollowupType).HasSnakeCaseEnum();
        builder.Property(entity => entity.Priority).HasSnakeCaseEnum();
        builder.Property(entity => entity.Status).HasSnakeCaseEnum();
        builder.Property(entity => entity.Metadata).IsJson("{}");
        builder.HasIndex(entity => new
            { entity.TenantId, entity.StudentUserId, entity.Status, entity.DueAt, entity.CreatedAt });
        builder.HasIndex(entity => new { entity.TenantId, entity.AssignedToUserId, entity.Status, entity.DueAt })
            .HasFilter("assigned_to_user_id is not null");
        builder.HasIndex(entity => new { entity.TenantId, entity.ClassId, entity.Status, entity.DueAt })
            .HasFilter("class_id is not null");

        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.StudentUserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.AssignedToUserId)
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<TenantClass>().WithMany()
            .HasForeignKey(entity => new { entity.TenantId, entity.ClassId })
            .HasPrincipalKey(entity => new { entity.TenantId, entity.Id })
            .OnDelete(DeleteBehavior.SetNull);
        builder.HasOne<User>().WithMany()
            .HasForeignKey(entity => entity.CompletedBy)
            .OnDelete(DeleteBehavior.SetNull);
    }
}