using FlatRender.IdentitySvc.Domain.Entities;
using FlatRender.IdentitySvc.Domain.Enums;
using Microsoft.EntityFrameworkCore;

namespace FlatRender.IdentitySvc.Infrastructure.Data;

public class IdentityDbContext(DbContextOptions<IdentityDbContext> options) : DbContext(options)
{
    // Tenants
    public DbSet<Tenant> Tenants => Set<Tenant>();
    public DbSet<TenantBranding> TenantBrandings => Set<TenantBranding>();
    public DbSet<TenantApiKey> TenantApiKeys => Set<TenantApiKey>();
    public DbSet<TenantWebhook> TenantWebhooks => Set<TenantWebhook>();
    public DbSet<TenantWebhookDelivery> TenantWebhookDeliveries => Set<TenantWebhookDelivery>();
    public DbSet<TenantUsageDaily> TenantUsageDailies => Set<TenantUsageDaily>();

    // Users
    public DbSet<User> Users => Set<User>();
    public DbSet<UserSession> UserSessions => Set<UserSession>();
    public DbSet<ConfirmationToken> ConfirmationTokens => Set<ConfirmationToken>();
    public DbSet<PushSubscription> PushSubscriptions => Set<PushSubscription>();
    public DbSet<MfaFactor> MfaFactors => Set<MfaFactor>();

    // Billing
    public DbSet<Plan> Plans => Set<Plan>();
    public DbSet<UserPlan> UserPlans => Set<UserPlan>();
    public DbSet<Payment> Payments => Set<Payment>();
    public DbSet<Discount> Discounts => Set<Discount>();
    public DbSet<UsedDiscount> UsedDiscounts => Set<UsedDiscount>();

    // CRM
    public DbSet<UserCrm> UserCrms => Set<UserCrm>();

    // OAuth providers
    public DbSet<OAuthConfig> OAuthConfigs => Set<OAuthConfig>();

    // Gamification
    public DbSet<Quest> Quests => Set<Quest>();
    public DbSet<UserQuestProgress> UserQuestProgresses => Set<UserQuestProgress>();
    public DbSet<Gift> Gifts => Set<Gift>();
    public DbSet<EarnedGift> EarnedGifts => Set<EarnedGift>();
    public DbSet<Avatar> Avatars => Set<Avatar>();

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.HasDefaultSchema("identity");

        // Native PostgreSQL enums are registered on the EF provider via npgsql.MapEnum<T>()
        // in Program.cs (the EF Core 9+ approach), which covers both the model and the
        // runtime ADO type mapping. No HasPostgresEnum<T>() calls are needed here.

        ConfigureTenants(mb);
        ConfigureUsers(mb);
        ConfigureBilling(mb);
        ConfigureGamification(mb);

        mb.Entity<UserCrm>(e =>
        {
            e.ToTable("user_crm");
            e.HasKey(x => x.UserId);
        });

        mb.Entity<OAuthConfig>(e =>
        {
            e.ToTable("oauth_config");
            e.HasKey(x => x.Provider);
        });
    }

    private static void ConfigureTenants(ModelBuilder mb)
    {
        mb.Entity<Tenant>(e =>
        {
            e.ToTable("tenants");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.Slug).HasColumnName("slug").HasColumnType("citext").IsRequired();
            e.Property(x => x.Name).HasColumnName("name").IsRequired();
            e.Property(x => x.Kind).HasColumnName("kind");
            e.Property(x => x.Status).HasColumnName("status");
            e.Property(x => x.CustomDomain).HasColumnName("custom_domain").HasColumnType("citext");
            e.Property(x => x.DomainVerified).HasColumnName("domain_verified");
            e.Property(x => x.AllowedOrigins).HasColumnName("allowed_origins");
            e.Property(x => x.ContactName).HasColumnName("contact_name");
            e.Property(x => x.ContactEmail).HasColumnName("contact_email").HasColumnType("citext");
            e.Property(x => x.ContactPhone).HasColumnName("contact_phone");
            e.Property(x => x.BillingEmail).HasColumnName("billing_email").HasColumnType("citext");
            e.Property(x => x.MaxUsers).HasColumnName("max_users");
            e.Property(x => x.MaxStorageGb).HasColumnName("max_storage_gb");
            e.Property(x => x.MonthlyRenderQty).HasColumnName("monthly_render_qty");
            e.Property(x => x.MonthlyRenderSec).HasColumnName("monthly_render_sec");
            e.Property(x => x.TrialEndsAt).HasColumnName("trial_ends_at");
            e.Property(x => x.SuspendedAt).HasColumnName("suspended_at");
            e.Property(x => x.SuspensionReason).HasColumnName("suspension_reason");
            e.Property(x => x.Metadata).HasColumnName("metadata").HasColumnType("jsonb");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.Property(x => x.DeletedAt).HasColumnName("deleted_at");
            e.HasIndex(x => x.Slug).IsUnique();
            e.HasOne(x => x.Branding).WithOne(b => b.Tenant).HasForeignKey<TenantBranding>(b => b.TenantId);
            e.HasMany(x => x.ApiKeys).WithOne(k => k.Tenant).HasForeignKey(k => k.TenantId);
            e.HasMany(x => x.Webhooks).WithOne(w => w.Tenant).HasForeignKey(w => w.TenantId);
        });

        mb.Entity<TenantBranding>(e =>
        {
            e.ToTable("tenant_branding");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.DisplayName).HasColumnName("display_name");
            e.Property(x => x.LogoUrl).HasColumnName("logo_url");
            e.Property(x => x.LogoDarkUrl).HasColumnName("logo_dark_url");
            e.Property(x => x.FaviconUrl).HasColumnName("favicon_url");
            e.Property(x => x.OgImageUrl).HasColumnName("og_image_url");
            e.Property(x => x.PrimaryColor).HasColumnName("primary_color");
            e.Property(x => x.SecondaryColor).HasColumnName("secondary_color");
            e.Property(x => x.AccentColor).HasColumnName("accent_color");
            e.Property(x => x.BackgroundColor).HasColumnName("background_color");
            e.Property(x => x.FontFamily).HasColumnName("font_family");
            e.Property(x => x.EmailFromName).HasColumnName("email_from_name");
            e.Property(x => x.EmailFromAddress).HasColumnName("email_from_address");
            e.Property(x => x.EmailReplyTo).HasColumnName("email_reply_to");
            e.Property(x => x.EmailFooterHtml).HasColumnName("email_footer_html");
            e.Property(x => x.SupportUrl).HasColumnName("support_url");
            e.Property(x => x.TermsUrl).HasColumnName("terms_url");
            e.Property(x => x.PrivacyUrl).HasColumnName("privacy_url");
            e.Property(x => x.EmbedEnabled).HasColumnName("embed_enabled");
            e.Property(x => x.EmbedAllowedHosts).HasColumnName("embed_allowed_hosts");
            e.Property(x => x.WatermarkText).HasColumnName("watermark_text");
            e.Property(x => x.WatermarkImageUrl).HasColumnName("watermark_image_url");
            e.Property(x => x.WatermarkEnabled).HasColumnName("watermark_enabled");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
        });

        mb.Entity<TenantApiKey>(e =>
        {
            e.ToTable("tenant_api_keys");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.CreatedByUserId).HasColumnName("created_by_user_id");
            e.Property(x => x.Name).HasColumnName("name");
            e.Property(x => x.Environment).HasColumnName("environment");
            e.Property(x => x.KeyPrefix).HasColumnName("key_prefix");
            e.Property(x => x.KeyHash).HasColumnName("key_hash");
            e.Property(x => x.Last4).HasColumnName("last4");
            e.Property(x => x.Scopes).HasColumnName("scopes");
            e.Property(x => x.AllowedIps).HasColumnName("allowed_ips");
            e.Property(x => x.RateLimitRpm).HasColumnName("rate_limit_rpm");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.ExpiresAt).HasColumnName("expires_at");
            e.Property(x => x.LastUsedAt).HasColumnName("last_used_at");
            e.Property(x => x.UsageCount).HasColumnName("usage_count");
            e.Property(x => x.RevokeReason).HasColumnName("revoke_reason");
            e.Property(x => x.RevokedAt).HasColumnName("revoked_at");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
        });

        mb.Entity<TenantWebhook>(e =>
        {
            e.ToTable("tenant_webhooks");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Name).HasColumnName("name");
            e.Property(x => x.Url).HasColumnName("url");
            e.Property(x => x.Events).HasColumnName("events");
            e.Property(x => x.SecretHash).HasColumnName("secret_hash");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.LastTriggeredAt).HasColumnName("last_triggered_at");
            e.Property(x => x.LastStatusCode).HasColumnName("last_status_code");
            e.Property(x => x.ConsecutiveFailures).HasColumnName("consecutive_failures");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.HasMany(x => x.Deliveries).WithOne(d => d.Webhook).HasForeignKey(d => d.WebhookId);
        });

        mb.Entity<TenantWebhookDelivery>(e =>
        {
            e.ToTable("tenant_webhook_deliveries");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.WebhookId).HasColumnName("webhook_id");
            e.Property(x => x.EventType).HasColumnName("event_type");
            e.Property(x => x.RequestUrl).HasColumnName("request_url");
            e.Property(x => x.RequestBody).HasColumnName("request_body");
            e.Property(x => x.ResponseStatus).HasColumnName("response_status");
            e.Property(x => x.ResponseBody).HasColumnName("response_body");
            e.Property(x => x.DurationMs).HasColumnName("duration_ms");
            e.Property(x => x.Attempt).HasColumnName("attempt");
            e.Property(x => x.Succeeded).HasColumnName("succeeded");
            e.Property(x => x.ErrorMessage).HasColumnName("error_message");
            e.Property(x => x.DeliveredAt).HasColumnName("delivered_at");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
        });

        mb.Entity<TenantUsageDaily>(e =>
        {
            e.ToTable("tenant_usage_daily");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.UsageDate).HasColumnName("usage_date");
            e.Property(x => x.RendersCompleted).HasColumnName("renders_completed");
            e.Property(x => x.RenderSeconds).HasColumnName("render_seconds");
            e.Property(x => x.StorageBytes).HasColumnName("storage_bytes");
            e.Property(x => x.ApiCalls).HasColumnName("api_calls");
            e.Property(x => x.ActiveUsers).HasColumnName("active_users");
            e.Property(x => x.AmountBilledMinor).HasColumnName("amount_billed_minor");
            e.Property(x => x.BillingCurrency).HasColumnName("billing_currency");
            e.Property(x => x.BillingStatus).HasColumnName("billing_status");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.HasIndex(x => new { x.TenantId, x.UsageDate }).IsUnique();
        });
    }

    private static void ConfigureUsers(ModelBuilder mb)
    {
        mb.Entity<User>(e =>
        {
            e.ToTable("users");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Email).HasColumnName("email").HasColumnType("citext");
            e.Property(x => x.EmailVerified).HasColumnName("email_verified");
            e.Property(x => x.EmailVerifiedAt).HasColumnName("email_verified_at");
            e.Property(x => x.PhoneNumber).HasColumnName("phone_number");
            e.Property(x => x.PhoneCountryCode).HasColumnName("phone_country_code");
            e.Property(x => x.PhoneVerified).HasColumnName("phone_verified");
            e.Property(x => x.PhoneVerifiedAt).HasColumnName("phone_verified_at");
            e.Property(x => x.PasswordHash).HasColumnName("password_hash");
            e.Property(x => x.PasswordSetAt).HasColumnName("password_set_at");
            e.Property(x => x.LastPasswordResetDate).HasColumnName("last_password_reset_date");
            e.Property(x => x.RegisterMode).HasColumnName("register_mode");
            e.Property(x => x.ExternalProvider).HasColumnName("external_provider");
            e.Property(x => x.ExternalProviderId).HasColumnName("external_provider_id");
            e.Property(x => x.FullName).HasColumnName("full_name");
            e.Property(x => x.AvatarUrl).HasColumnName("avatar_url");
            e.Property(x => x.BirthDate).HasColumnName("birth_date");
            e.Property(x => x.Gender).HasColumnName("gender");
            e.Property(x => x.NationalCode).HasColumnName("national_code");
            e.Property(x => x.CountryCode).HasColumnName("country_code");
            e.Property(x => x.CompanyName).HasColumnName("company_name");
            e.Property(x => x.WebsiteName).HasColumnName("website_name");
            e.Property(x => x.Slogan).HasColumnName("slogan");
            e.Property(x => x.AboutMe).HasColumnName("about_me");
            e.Property(x => x.MethodOfIntroduction).HasColumnName("method_of_introduction");
            e.Property(x => x.BalanceMinor).HasColumnName("balance_minor");
            e.Property(x => x.AffiliateBalanceMinor).HasColumnName("affiliate_balance_minor");
            e.Property(x => x.AffiliateOwnerId).HasColumnName("affiliate_owner_id");
            e.Property(x => x.ProfitPercentage).HasColumnName("profit_percentage");
            e.Property(x => x.LoyaltyScore).HasColumnName("loyalty_score");
            e.Property(x => x.PurplePoint).HasColumnName("purple_point");
            e.Property(x => x.DailyRemainRenderCount).HasColumnName("daily_remain_render_count");
            e.Property(x => x.MaxDailyRenderCount).HasColumnName("max_daily_render_count");
            e.Property(x => x.ParallelRenderingCeiling).HasColumnName("parallel_rendering_ceiling");
            e.Property(x => x.UserDailyFreeChargeSec).HasColumnName("user_daily_free_charge_sec");
            e.Property(x => x.DailyFreeChargeResetDate).HasColumnName("daily_free_charge_reset_date");
            e.Property(x => x.MaxPreviewDurationSec).HasColumnName("max_preview_duration_sec");
            e.Property(x => x.ForceRenderQueue).HasColumnName("force_render_queue");
            e.Property(x => x.RemoveWatermarkService).HasColumnName("remove_watermark_service");
            e.Property(x => x.TelegramId).HasColumnName("telegram_id");
            e.Property(x => x.TelegramTellMe).HasColumnName("telegram_tell_me");
            e.Property(x => x.EmailTellMe).HasColumnName("email_tell_me");
            e.Property(x => x.SmsTellMe).HasColumnName("sms_tell_me");
            e.Property(x => x.PushTellMe).HasColumnName("push_tell_me");
            e.Property(x => x.StorageEndpoint).HasColumnName("storage_endpoint");
            e.Property(x => x.UsedStorageBytes).HasColumnName("used_storage_bytes");
            e.Property(x => x.IsAdmin).HasColumnName("is_admin");
            e.Property(x => x.IsTenantAdmin).HasColumnName("is_tenant_admin");
            e.Property(x => x.BanAccount).HasColumnName("ban_account");
            e.Property(x => x.BanReason).HasColumnName("ban_reason");
            e.Property(x => x.UnblockDate).HasColumnName("unblock_date");
            e.Property(x => x.LastActiveDate).HasColumnName("last_active_date");
            e.Property(x => x.LastLoginAt).HasColumnName("last_login_at");
            e.Property(x => x.LastLoginIp).HasColumnName("last_login_ip");
            e.Property(x => x.RegisteredWithMobileApp).HasColumnName("registered_with_mobile_app");
            e.Property(x => x.RegisterDate).HasColumnName("register_date");
            e.Property(x => x.Metadata).HasColumnName("metadata").HasColumnType("jsonb");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.Property(x => x.DeletedAt).HasColumnName("deleted_at");
            e.HasIndex(x => new { x.TenantId, x.Email }).IsUnique();
            e.HasMany(x => x.Sessions).WithOne(s => s.User).HasForeignKey(s => s.UserId).OnDelete(DeleteBehavior.Cascade);
            e.HasMany(x => x.MfaFactors).WithOne(m => m.User).HasForeignKey(m => m.UserId).OnDelete(DeleteBehavior.Cascade);
            e.HasMany(x => x.PushSubscriptions).WithOne(p => p.User).HasForeignKey(p => p.UserId).OnDelete(DeleteBehavior.Cascade);
        });

        mb.Entity<UserSession>(e =>
        {
            e.ToTable("user_sessions");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.RefreshTokenHash).HasColumnName("refresh_token_hash");
            e.Property(x => x.DeviceId).HasColumnName("device_id");
            e.Property(x => x.DeviceName).HasColumnName("device_name");
            e.Property(x => x.UserAgent).HasColumnName("user_agent");
            e.Property(x => x.IpAddress).HasColumnName("ip_address");
            e.Property(x => x.IssuedAt).HasColumnName("issued_at");
            e.Property(x => x.ExpiresAt).HasColumnName("expires_at");
            e.Property(x => x.RevokedAt).HasColumnName("revoked_at");
            e.Property(x => x.LastUsedAt).HasColumnName("last_used_at");
            e.HasIndex(x => x.RefreshTokenHash).IsUnique();
        });

        mb.Entity<ConfirmationToken>(e =>
        {
            e.ToTable("confirmation_tokens");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Purpose).HasColumnName("purpose");
            e.Property(x => x.Identifier).HasColumnName("identifier").HasColumnType("citext");
            e.Property(x => x.NextIdentifier).HasColumnName("next_identifier").HasColumnType("citext");
            e.Property(x => x.TokenHash).HasColumnName("token_hash");
            e.Property(x => x.Code).HasColumnName("code");
            e.Property(x => x.IsConsumed).HasColumnName("is_consumed");
            e.Property(x => x.ConsumedAt).HasColumnName("consumed_at");
            e.Property(x => x.TryCount).HasColumnName("try_count");
            e.Property(x => x.MaxTries).HasColumnName("max_tries");
            e.Property(x => x.RequestIp).HasColumnName("request_ip");
            e.Property(x => x.ExpiresAt).HasColumnName("expires_at");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
        });

        mb.Entity<PushSubscription>(e =>
        {
            e.ToTable("push_subscriptions");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Endpoint).HasColumnName("endpoint");
            e.Property(x => x.P256dhKey).HasColumnName("p256dh_key");
            e.Property(x => x.AuthKey).HasColumnName("auth_key");
            e.Property(x => x.UserAgent).HasColumnName("user_agent");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.LastUsedAt).HasColumnName("last_used_at");
            e.Property(x => x.FailureCount).HasColumnName("failure_count");
            e.Property(x => x.LastFailureAt).HasColumnName("last_failure_at");
            e.Property(x => x.LastFailureStatus).HasColumnName("last_failure_status");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.HasIndex(x => new { x.UserId, x.Endpoint }).IsUnique();
        });

        mb.Entity<MfaFactor>(e =>
        {
            e.ToTable("mfa_factors");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.FactorType).HasColumnName("factor_type");
            e.Property(x => x.SecretEncrypted).HasColumnName("secret_encrypted");
            e.Property(x => x.IsVerified).HasColumnName("is_verified");
            e.Property(x => x.IsPrimary).HasColumnName("is_primary");
            e.Property(x => x.Label).HasColumnName("label");
            e.Property(x => x.LastUsedAt).HasColumnName("last_used_at");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
        });
    }

    private static void ConfigureBilling(ModelBuilder mb)
    {
        mb.Entity<Plan>(e =>
        {
            e.ToTable("plans");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Scope).HasColumnName("scope");
            e.Property(x => x.Code).HasColumnName("code");
            e.Property(x => x.Name).HasColumnName("name");
            e.Property(x => x.Description).HasColumnName("description");
            e.Property(x => x.PriceMinor).HasColumnName("price_minor");
            e.Property(x => x.BeforePriceMinor).HasColumnName("before_price_minor");
            e.Property(x => x.Currency).HasColumnName("currency");
            e.Property(x => x.DiscountPercentage).HasColumnName("discount_percentage");
            e.Property(x => x.BillingPeriod).HasColumnName("billing_period");
            e.Property(x => x.MonthsDuration).HasColumnName("months_duration");
            e.Property(x => x.SecondsCharge).HasColumnName("seconds_charge");
            e.Property(x => x.MonthlyRendersQuota).HasColumnName("monthly_renders_quota");
            e.Property(x => x.StorageGb).HasColumnName("storage_gb");
            e.Property(x => x.ParallelRenders).HasColumnName("parallel_renders");
            e.Property(x => x.MaxResolution).HasColumnName("max_resolution");
            e.Property(x => x.MinVideoLengthSec).HasColumnName("min_video_length_sec");
            e.Property(x => x.RenderSpeedFactor).HasColumnName("render_speed_factor");
            e.Property(x => x.Sort).HasColumnName("sort");
            e.Property(x => x.Icon).HasColumnName("icon");
            e.Property(x => x.Cover).HasColumnName("cover");
            e.Property(x => x.Color).HasColumnName("color");
            e.Property(x => x.IsFeatured).HasColumnName("is_featured");
            e.Property(x => x.Features).HasColumnName("features").HasColumnType("jsonb");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.AvailableFrom).HasColumnName("available_from");
            e.Property(x => x.AvailableUntil).HasColumnName("available_until");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.Property(x => x.DeletedAt).HasColumnName("deleted_at");
        });

        mb.Entity<UserPlan>(e =>
        {
            e.ToTable("user_plans");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.PlanId).HasColumnName("plan_id");
            e.Property(x => x.PlanCode).HasColumnName("plan_code");
            e.Property(x => x.PlanName).HasColumnName("plan_name");
            e.Property(x => x.PriceMinorPaid).HasColumnName("price_minor_paid");
            e.Property(x => x.Currency).HasColumnName("currency");
            e.Property(x => x.InitialSecondsCharge).HasColumnName("initial_seconds_charge");
            e.Property(x => x.RemainChargeSec).HasColumnName("remain_charge_sec");
            e.Property(x => x.AddedChargeFromPastPlan).HasColumnName("added_charge_from_past_plan");
            e.Property(x => x.MonthlyRendersUsed).HasColumnName("monthly_renders_used");
            e.Property(x => x.MonthlyRendersResetAt).HasColumnName("monthly_renders_reset_at");
            e.Property(x => x.RegisterDate).HasColumnName("register_date");
            e.Property(x => x.StartsAt).HasColumnName("starts_at");
            e.Property(x => x.ExpiresAt).HasColumnName("expires_at");
            e.Property(x => x.CancelledAt).HasColumnName("cancelled_at");
            e.Property(x => x.CancelReason).HasColumnName("cancel_reason");
            e.Property(x => x.AutoRenew).HasColumnName("auto_renew");
            e.Property(x => x.PaymentId).HasColumnName("payment_id");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.HasOne(x => x.User).WithMany(u => u.Plans).HasForeignKey(x => x.UserId);
            e.HasOne(x => x.Plan).WithMany(p => p.UserPlans).HasForeignKey(x => x.PlanId);
        });

        mb.Entity<Payment>(e =>
        {
            e.ToTable("payments");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.Gateway).HasColumnName("gateway");
            e.Property(x => x.Status).HasColumnName("status");
            e.Property(x => x.Action).HasColumnName("action");
            e.Property(x => x.AmountMinor).HasColumnName("amount_minor");
            e.Property(x => x.Currency).HasColumnName("currency");
            e.Property(x => x.BalanceReducerMinor).HasColumnName("balance_reducer_minor");
            e.Property(x => x.DiscountValueMinor).HasColumnName("discount_value_minor");
            e.Property(x => x.GatewayToken).HasColumnName("gateway_token");
            e.Property(x => x.GatewayOrderId).HasColumnName("gateway_order_id");
            e.Property(x => x.GatewayTrackId).HasColumnName("gateway_track_id");
            e.Property(x => x.GatewayResponse).HasColumnName("gateway_response").HasColumnType("jsonb");
            e.Property(x => x.CardLast4).HasColumnName("card_last4");
            e.Property(x => x.CardHash).HasColumnName("card_hash");
            e.Property(x => x.Title).HasColumnName("title");
            e.Property(x => x.Description).HasColumnName("description");
            e.Property(x => x.OwnerUserId).HasColumnName("owner_user_id");
            e.Property(x => x.AffiliateProfitMinor).HasColumnName("affiliate_profit_minor");
            e.Property(x => x.UsedDiscountId).HasColumnName("used_discount_id");
            e.Property(x => x.PlanId).HasColumnName("plan_id");
            e.Property(x => x.RenderJobId).HasColumnName("render_job_id");
            e.Property(x => x.UserProjectId).HasColumnName("user_project_id");
            e.Property(x => x.ConfirmedAt).HasColumnName("confirmed_at");
            e.Property(x => x.FailedAt).HasColumnName("failed_at");
            e.Property(x => x.FailureReason).HasColumnName("failure_reason");
            e.Property(x => x.RefundedAt).HasColumnName("refunded_at");
            e.Property(x => x.RefundAmountMinor).HasColumnName("refund_amount_minor");
            e.Property(x => x.RefundReason).HasColumnName("refund_reason");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.HasOne(x => x.User).WithMany(u => u.Payments).HasForeignKey(x => x.UserId);
        });

        mb.Entity<Discount>(e =>
        {
            e.ToTable("discounts");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Name).HasColumnName("name");
            e.Property(x => x.Code).HasColumnName("code").HasColumnType("citext");
            e.Property(x => x.Kind).HasColumnName("kind");
            e.Property(x => x.Value).HasColumnName("value");
            e.Property(x => x.OwnerUserId).HasColumnName("owner_user_id");
            e.Property(x => x.OwnerProfitPercentage).HasColumnName("owner_profit_percentage");
            e.Property(x => x.OnlyOwner).HasColumnName("only_owner");
            e.Property(x => x.MaxUseCount).HasColumnName("max_use_count");
            e.Property(x => x.UsedCount).HasColumnName("used_count");
            e.Property(x => x.MinPurchaseMinor).HasColumnName("min_purchase_minor");
            e.Property(x => x.AppliesToPlanIds).HasColumnName("applies_to_plan_ids");
            e.Property(x => x.StartsAt).HasColumnName("starts_at");
            e.Property(x => x.ExpiresAt).HasColumnName("expires_at");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.HasIndex(x => new { x.TenantId, x.Code }).IsUnique();
        });

        mb.Entity<UsedDiscount>(e =>
        {
            e.ToTable("used_discounts");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.DiscountId).HasColumnName("discount_id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.PaymentId).HasColumnName("payment_id");
            e.Property(x => x.Code).HasColumnName("code").HasColumnType("citext");
            e.Property(x => x.AmountDiscountedMinor).HasColumnName("amount_discounted_minor");
            e.Property(x => x.UseDate).HasColumnName("use_date");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
        });
    }

    private static void ConfigureGamification(ModelBuilder mb)
    {
        mb.Entity<Quest>(e =>
        {
            e.ToTable("quests");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Title).HasColumnName("title");
            e.Property(x => x.Challenge).HasColumnName("challenge");
            e.Property(x => x.Why).HasColumnName("why");
            e.Property(x => x.Hint).HasColumnName("hint");
            e.Property(x => x.Aphorism).HasColumnName("aphorism");
            e.Property(x => x.Icon).HasColumnName("icon");
            e.Property(x => x.QuestType).HasColumnName("quest_type");
            e.Property(x => x.TargetEvent).HasColumnName("target_event");
            e.Property(x => x.TargetCount).HasColumnName("target_count");
            e.Property(x => x.Metadata).HasColumnName("metadata").HasColumnType("jsonb");
            e.Property(x => x.PrizeType).HasColumnName("prize_type");
            e.Property(x => x.PrizeAmount).HasColumnName("prize_amount");
            e.Property(x => x.LevelLimit).HasColumnName("level_limit");
            e.Property(x => x.StartUrl).HasColumnName("start_url");
            e.Property(x => x.PostActionName).HasColumnName("post_action_name");
            e.Property(x => x.OrderValue).HasColumnName("order_value");
            e.Property(x => x.StartsAt).HasColumnName("starts_at");
            e.Property(x => x.ExpiresAt).HasColumnName("expires_at");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.HasMany(x => x.Progress).WithOne(p => p.Quest).HasForeignKey(p => p.QuestId);
        });

        mb.Entity<UserQuestProgress>(e =>
        {
            e.ToTable("user_quest_progress");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.QuestId).HasColumnName("quest_id");
            e.Property(x => x.CurrentCount).HasColumnName("current_count");
            e.Property(x => x.TextValue).HasColumnName("text_value");
            e.Property(x => x.IsCompleted).HasColumnName("is_completed");
            e.Property(x => x.CompletedAt).HasColumnName("completed_at");
            e.Property(x => x.PrizeClaimed).HasColumnName("prize_claimed");
            e.Property(x => x.PrizeClaimedAt).HasColumnName("prize_claimed_at");
            e.Property(x => x.PeriodStart).HasColumnName("period_start");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
            e.HasIndex(x => new { x.UserId, x.QuestId, x.PeriodStart }).IsUnique();
        });

        mb.Entity<Gift>(e =>
        {
            e.ToTable("gifts");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.TenantId).HasColumnName("tenant_id");
            e.Property(x => x.Name).HasColumnName("name");
            e.Property(x => x.Description).HasColumnName("description");
            e.Property(x => x.Icon).HasColumnName("icon");
            e.Property(x => x.GiftType).HasColumnName("gift_type");
            e.Property(x => x.PrizeType).HasColumnName("prize_type");
            e.Property(x => x.Value).HasColumnName("value");
            e.Property(x => x.Unit).HasColumnName("unit");
            e.Property(x => x.AssignedByUserId).HasColumnName("assigned_by_user_id");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.HasMany(x => x.EarnedGifts).WithOne(eg => eg.Gift).HasForeignKey(eg => eg.GiftId);
        });

        mb.Entity<EarnedGift>(e =>
        {
            e.ToTable("earned_gifts");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.UserId).HasColumnName("user_id");
            e.Property(x => x.GiftId).HasColumnName("gift_id");
            e.Property(x => x.NotificationId).HasColumnName("notification_id");
            e.Property(x => x.Source).HasColumnName("source");
            e.Property(x => x.SourceRef).HasColumnName("source_ref");
            e.Property(x => x.EarnedAt).HasColumnName("earned_at");
            e.Property(x => x.ExpiresAt).HasColumnName("expires_at");
            e.Property(x => x.IsUsed).HasColumnName("is_used");
            e.Property(x => x.UsedAt).HasColumnName("used_at");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
        });

        mb.Entity<Avatar>(e =>
        {
            e.ToTable("avatars");
            e.HasKey(x => x.Id);
            e.Property(x => x.Id).HasColumnName("id");
            e.Property(x => x.Code).HasColumnName("code");
            e.Property(x => x.Url).HasColumnName("url");
            e.Property(x => x.Description).HasColumnName("description");
            e.Property(x => x.Sort).HasColumnName("sort");
            e.Property(x => x.IsActive).HasColumnName("is_active");
            e.Property(x => x.CreatedAt).HasColumnName("created_at");
            e.HasIndex(x => x.Code).IsUnique();
        });
    }
}
