using MemberCenter.Domain.Entities;
using MemberCenter.Infrastructure.Identity;
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace MemberCenter.Infrastructure.Persistence;

public class MemberCenterDbContext
    : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>, IDataProtectionKeyContext
{
    public MemberCenterDbContext(DbContextOptions<MemberCenterDbContext> options)
        : base(options)
    {
    }

    public DbSet<Tenant> Tenants => Set<Tenant>();
    public DbSet<TenantManager> TenantManagers => Set<TenantManager>();
    public DbSet<NewsletterList> NewsletterLists => Set<NewsletterList>();
    public DbSet<NewsletterSubscription> NewsletterSubscriptions => Set<NewsletterSubscription>();
    public DbSet<UserProfile> UserProfiles => Set<UserProfile>();
    public DbSet<UserProfileImage> UserProfileImages => Set<UserProfileImage>();
    public DbSet<UserAddress> UserAddresses => Set<UserAddress>();
    public DbSet<EmailBlacklist> EmailBlacklist => Set<EmailBlacklist>();
    public DbSet<EmailVerification> EmailVerifications => Set<EmailVerification>();
    public DbSet<UnsubscribeToken> UnsubscribeTokens => Set<UnsubscribeToken>();
    public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
    public DbSet<SystemFlag> SystemFlags => Set<SystemFlag>();
    public DbSet<AuthResource> AuthResources => Set<AuthResource>();
    public DbSet<AuthResourceScope> AuthResourceScopes => Set<AuthResourceScope>();
    public DbSet<AuthClientUsagePermission> AuthClientUsagePermissions => Set<AuthClientUsagePermission>();
    public DbSet<AdminPermission> AdminPermissions => Set<AdminPermission>();
    public DbSet<AdminRolePermission> AdminRolePermissions => Set<AdminRolePermission>();
    public DbSet<FileAccessDownloadToken> FileAccessDownloadTokens => Set<FileAccessDownloadToken>();
    public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = null!;

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.UseOpenIddict();

        builder.Entity<Tenant>(entity =>
        {
            entity.ToTable("tenants");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Name).IsRequired();
            entity.Property(x => x.Status).IsRequired().HasDefaultValue("active");
            entity.Property(x => x.Domains).HasColumnType("text[]");
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
        });

        builder.Entity<TenantManager>(entity =>
        {
            entity.ToTable("tenant_managers");
            entity.HasKey(x => new { x.TenantId, x.UserId });
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.HasIndex(x => x.UserId).HasDatabaseName("idx_tenant_managers_user_id");
            entity.HasOne(x => x.Tenant)
                .WithMany(x => x.Managers)
                .HasForeignKey(x => x.TenantId)
                .OnDelete(DeleteBehavior.Cascade);
            entity.HasOne<ApplicationUser>()
                .WithMany()
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
            entity.HasOne<ApplicationUser>()
                .WithMany()
                .HasForeignKey(x => x.CreatedBy)
                .OnDelete(DeleteBehavior.SetNull);
        });

        builder.Entity<NewsletterList>(entity =>
        {
            entity.ToTable("newsletter_lists");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Name).IsRequired();
            entity.Property(x => x.Status).IsRequired().HasDefaultValue("active");
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.HasOne(x => x.Tenant)
                .WithMany(t => t.NewsletterLists)
                .HasForeignKey(x => x.TenantId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<NewsletterSubscription>(entity =>
        {
            entity.ToTable("newsletter_subscriptions");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Email).IsRequired();
            entity.Property(x => x.Status).IsRequired().HasDefaultValue("pending");
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.Property(x => x.Preferences)
                .HasColumnType("jsonb")
                .HasConversion(
                    v => v.RootElement.GetRawText(),
                    v => System.Text.Json.JsonDocument.Parse(v, new System.Text.Json.JsonDocumentOptions()));
            entity.HasIndex(x => x.Email).HasDatabaseName("idx_newsletter_subscriptions_email");
            entity.HasIndex(x => x.ListId).HasDatabaseName("idx_newsletter_subscriptions_list_id");
            entity.HasIndex(x => new { x.ListId, x.Email }).IsUnique();
            entity.HasOne(x => x.List)
                .WithMany(l => l.Subscriptions)
                .HasForeignKey(x => x.ListId)
                .OnDelete(DeleteBehavior.Cascade);
            entity.HasOne<ApplicationUser>()
                .WithMany()
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.SetNull);
        });

        builder.Entity<UserProfile>(entity =>
        {
            entity.ToTable("user_profiles");
            entity.HasKey(x => x.UserId);
            entity.Property(x => x.LastName).IsRequired().HasMaxLength(100);
            entity.Property(x => x.FirstName).IsRequired().HasMaxLength(100);
            entity.Property(x => x.NickName).HasMaxLength(100);
            entity.Property(x => x.MobilePhone).HasMaxLength(50);
            entity.Property(x => x.LandlinePhone).HasMaxLength(50);
            entity.Property(x => x.Gender).IsRequired().HasMaxLength(20).HasDefaultValue("unspecified");
            entity.Property(x => x.CompanyName).HasMaxLength(200);
            entity.Property(x => x.Department).HasMaxLength(200);
            entity.Property(x => x.JobTitle).HasMaxLength(200);
            entity.Property(x => x.CompanyPhone).HasMaxLength(50);
            entity.Property(x => x.TaxId).HasMaxLength(32);
            entity.Property(x => x.InvoiceTitle).HasMaxLength(200);
            entity.Property(x => x.Remark).HasMaxLength(1000);
            entity.Property(x => x.UpdatedAt).HasDefaultValueSql("now()");
            entity.HasOne<ApplicationUser>()
                .WithOne(x => x.Profile)
                .HasForeignKey<UserProfile>(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<UserProfileImage>(entity =>
        {
            entity.ToTable("user_profile_images");
            entity.HasKey(x => x.UserId);
            entity.Property(x => x.Content).IsRequired().HasColumnType("bytea");
            entity.Property(x => x.ContentType).IsRequired().HasMaxLength(100);
            entity.Property(x => x.UpdatedAt).HasDefaultValueSql("now()");
            entity.HasOne<ApplicationUser>()
                .WithOne()
                .HasForeignKey<UserProfileImage>(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<UserAddress>(entity =>
        {
            entity.ToTable("user_addresses");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Label).IsRequired().HasMaxLength(100);
            entity.Property(x => x.RecipientName).IsRequired().HasMaxLength(100);
            entity.Property(x => x.RecipientPhone).IsRequired().HasMaxLength(50);
            entity.Property(x => x.CountryCode).IsRequired().HasMaxLength(2);
            entity.Property(x => x.PostalCode).HasMaxLength(20);
            entity.Property(x => x.StateRegion).HasMaxLength(100);
            entity.Property(x => x.City).HasMaxLength(100);
            entity.Property(x => x.District).HasMaxLength(100);
            entity.Property(x => x.AddressLine1).IsRequired().HasMaxLength(255);
            entity.Property(x => x.AddressLine2).HasMaxLength(255);
            entity.Property(x => x.CompanyName).HasMaxLength(200);
            entity.Property(x => x.IsDefault).HasDefaultValue(false);
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.Property(x => x.UpdatedAt).HasDefaultValueSql("now()");
            entity.Property(x => x.AddressMetaJson)
                .HasColumnType("jsonb")
                .HasConversion(
                    v => v == null ? null : v.RootElement.GetRawText(),
                    v => string.IsNullOrWhiteSpace(v) ? null : System.Text.Json.JsonDocument.Parse(v, new System.Text.Json.JsonDocumentOptions()));
            entity.HasIndex(x => x.UserId).HasDatabaseName("idx_user_addresses_user_id");
            entity.HasIndex(x => new { x.UserId, x.IsDefault })
                .IsUnique()
                .HasFilter("\"IsDefault\" = true")
                .HasDatabaseName("ux_user_addresses_default_per_user");
            entity.HasOne<ApplicationUser>()
                .WithMany(x => x.Addresses)
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<EmailBlacklist>(entity =>
        {
            entity.ToTable("email_blacklist");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Email).IsRequired();
            entity.Property(x => x.Reason).IsRequired();
            entity.Property(x => x.BlacklistedAt).HasDefaultValueSql("now()");
            entity.Property(x => x.BlacklistedBy).IsRequired();
            entity.HasIndex(x => x.Email).IsUnique();
        });

        builder.Entity<EmailVerification>(entity =>
        {
            entity.ToTable("email_verifications");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Email).IsRequired();
            entity.Property(x => x.TokenHash).IsRequired();
            entity.Property(x => x.Purpose).IsRequired();
            entity.HasIndex(x => x.Email).HasDatabaseName("idx_email_verifications_email");
            entity.HasOne<Tenant>()
                .WithMany()
                .HasForeignKey(x => x.TenantId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<UnsubscribeToken>(entity =>
        {
            entity.ToTable("unsubscribe_tokens");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.TokenHash).IsRequired();
            entity.HasOne(x => x.Subscription)
                .WithMany()
                .HasForeignKey(x => x.SubscriptionId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<AuditLog>(entity =>
        {
            entity.ToTable("audit_logs");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.ActorType).IsRequired();
            entity.Property(x => x.Action).IsRequired();
            entity.Property(x => x.Payload)
                .HasColumnType("jsonb")
                .HasConversion(
                    v => v.RootElement.GetRawText(),
                    v => System.Text.Json.JsonDocument.Parse(v, new System.Text.Json.JsonDocumentOptions()));
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
        });

        builder.Entity<SystemFlag>(entity =>
        {
            entity.ToTable("system_flags");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Key).IsRequired();
            entity.Property(x => x.Value).IsRequired();
            entity.Property(x => x.UpdatedAt).HasDefaultValueSql("now()");
            entity.HasIndex(x => x.Key).IsUnique();
        });

        builder.Entity<AuthResource>(entity =>
        {
            entity.ToTable("auth_resources");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Name).IsRequired().HasMaxLength(100);
            entity.Property(x => x.Audience).IsRequired().HasMaxLength(200);
            entity.Property(x => x.Description).HasMaxLength(500);
            entity.Property(x => x.RequireTenant).HasDefaultValue(false);
            entity.Property(x => x.AllowDelegatedToken).HasDefaultValue(false);
            entity.Property(x => x.IsEnabled).HasDefaultValue(true);
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.Property(x => x.UpdatedAt).HasDefaultValueSql("now()");
            entity.HasIndex(x => x.Name).IsUnique();
            entity.HasIndex(x => x.Audience).IsUnique();
        });

        builder.Entity<AuthResourceScope>(entity =>
        {
            entity.ToTable("auth_resource_scopes");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Scope).IsRequired().HasMaxLength(200);
            entity.Property(x => x.Description).HasMaxLength(500);
            entity.Property(x => x.IsEnabled).HasDefaultValue(true);
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.HasIndex(x => new { x.ResourceId, x.Scope }).IsUnique();
            entity.HasIndex(x => x.Scope).HasDatabaseName("idx_auth_resource_scopes_scope");
            entity.HasOne(x => x.Resource)
                .WithMany(x => x.Scopes)
                .HasForeignKey(x => x.ResourceId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<AuthClientUsagePermission>(entity =>
        {
            entity.ToTable("auth_client_usage_permissions");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Usage).IsRequired().HasMaxLength(100);
            entity.Property(x => x.Scope).IsRequired().HasMaxLength(200);
            entity.Property(x => x.IsEnabled).HasDefaultValue(true);
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.HasIndex(x => new { x.Usage, x.Scope }).IsUnique();
        });

        builder.Entity<AdminPermission>(entity =>
        {
            entity.ToTable("admin_permissions");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Key).IsRequired().HasMaxLength(200);
            entity.Property(x => x.Name).IsRequired().HasMaxLength(200);
            entity.Property(x => x.Description).HasMaxLength(1000);
            entity.Property(x => x.IsEnabled).HasDefaultValue(true);
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.Property(x => x.UpdatedAt).HasDefaultValueSql("now()");
            entity.HasIndex(x => x.Key).IsUnique();
        });

        builder.Entity<AdminRolePermission>(entity =>
        {
            entity.ToTable("admin_role_permissions");
            entity.HasKey(x => new { x.RoleId, x.PermissionId });
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.HasOne<ApplicationRole>()
                .WithMany()
                .HasForeignKey(x => x.RoleId)
                .OnDelete(DeleteBehavior.Cascade);
            entity.HasOne(x => x.Permission)
                .WithMany(x => x.RolePermissions)
                .HasForeignKey(x => x.PermissionId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<FileAccessDownloadToken>(entity =>
        {
            entity.ToTable("file_access_download_tokens");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.TokenHash).IsRequired();
            entity.Property(x => x.FileId).HasMaxLength(200);
            entity.Property(x => x.ObjectKey).HasMaxLength(1000);
            entity.Property(x => x.Method).IsRequired().HasMaxLength(10).HasDefaultValue("GET");
            entity.Property(x => x.Scope).IsRequired().HasMaxLength(200).HasDefaultValue("files:download.read");
            entity.Property(x => x.IssuedByClientId).HasMaxLength(200);
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.HasIndex(x => x.TokenHash).IsUnique();
            entity.HasIndex(x => x.ExpiresAt).HasDatabaseName("idx_file_access_download_tokens_expires_at");
            entity.HasIndex(x => new { x.TenantId, x.UserId }).HasDatabaseName("idx_file_access_download_tokens_tenant_user");
            entity.HasOne<Tenant>()
                .WithMany()
                .HasForeignKey(x => x.TenantId)
                .OnDelete(DeleteBehavior.Cascade);
            entity.HasOne<ApplicationUser>()
                .WithMany()
                .HasForeignKey(x => x.UserId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<ApplicationUser>(entity =>
        {
            entity.ToTable("users");
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
            entity.Property(x => x.IsBlacklisted).HasDefaultValue(false);
        });

        builder.Entity<ApplicationRole>(entity =>
        {
            entity.ToTable("roles");
            entity.Property(x => x.CreatedAt).HasDefaultValueSql("now()");
        });

        builder.Entity<Microsoft.AspNetCore.Identity.IdentityUserRole<Guid>>(entity =>
        {
            entity.ToTable("user_roles");
        });

        builder.Entity<Microsoft.AspNetCore.Identity.IdentityUserClaim<Guid>>(entity =>
        {
            entity.ToTable("user_claims");
        });

        builder.Entity<Microsoft.AspNetCore.Identity.IdentityRoleClaim<Guid>>(entity =>
        {
            entity.ToTable("role_claims");
        });

        builder.Entity<Microsoft.AspNetCore.Identity.IdentityUserLogin<Guid>>(entity =>
        {
            entity.ToTable("user_logins");
        });

        builder.Entity<Microsoft.AspNetCore.Identity.IdentityUserToken<Guid>>(entity =>
        {
            entity.ToTable("user_tokens");
        });

        builder.Entity<Microsoft.AspNetCore.Identity.IdentityUserLogin<Guid>>().HasKey(l => new { l.LoginProvider, l.ProviderKey });
        builder.Entity<Microsoft.AspNetCore.Identity.IdentityUserToken<Guid>>().HasKey(t => new { t.UserId, t.LoginProvider, t.Name });
    }
}
