using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using ROLAC.API.Data.Logging;
using ROLAC.API.Entities;
using ROLAC.API.Entities.Notifications;

namespace ROLAC.API.Data;

public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<RefreshToken>    RefreshTokens    => Set<RefreshToken>();
    public DbSet<UserInvitation>  UserInvitations  => Set<UserInvitation>();
    public DbSet<Member>       Members       => Set<Member>();
    public DbSet<FamilyUnit>   FamilyUnits   => Set<FamilyUnit>();
    public DbSet<GivingCategory>  GivingCategories  => Set<GivingCategory>();
    public DbSet<OfferingSession> OfferingSessions  => Set<OfferingSession>();
    public DbSet<Giving>          Givings           => Set<Giving>();
    public DbSet<Ministry>        Ministries        => Set<Ministry>();
    public DbSet<ExpenseCategoryGroup> ExpenseCategoryGroups => Set<ExpenseCategoryGroup>();
    public DbSet<ExpenseSubCategory>   ExpenseSubCategories  => Set<ExpenseSubCategory>();
    public DbSet<Form990ExpenseLine> Form990ExpenseLines => Set<Form990ExpenseLine>();
    public DbSet<Payee1099>          Payee1099s           => Set<Payee1099>();
    public DbSet<Form1099Box>        Form1099Boxes        => Set<Form1099Box>();
    public DbSet<Expense>          Expenses          => Set<Expense>();
    public DbSet<ExpenseLine>      ExpenseLines      => Set<ExpenseLine>();
    public DbSet<ExpenseSnapshot>     ExpenseSnapshots     => Set<ExpenseSnapshot>();
    public DbSet<ExpenseSnapshotLine> ExpenseSnapshotLines => Set<ExpenseSnapshotLine>();
    public DbSet<MonthlyStatement> MonthlyStatements => Set<MonthlyStatement>();
    public DbSet<ChurchProfile>    ChurchProfiles    => Set<ChurchProfile>();
    public DbSet<Check>            Checks            => Set<Check>();
    public DbSet<CheckLine>        CheckLines        => Set<CheckLine>();
    public DbSet<MealAttendance>   MealAttendances   => Set<MealAttendance>();
    public DbSet<RolePermission>   RolePermissions   => Set<RolePermission>();

    public DbSet<MemberChannelBinding> MemberChannelBindings => Set<MemberChannelBinding>();
    public DbSet<LineBindingCode>      LineBindingCodes      => Set<LineBindingCode>();
    public DbSet<MessagingGroup>       MessagingGroups       => Set<MessagingGroup>();
    public DbSet<NotificationLog>      NotificationLogs      => Set<NotificationLog>();
    public DbSet<WebPushSubscription>  WebPushSubscriptions  => Set<WebPushSubscription>();

    public DbSet<SiteSetting>          SiteSettings          => Set<SiteSetting>();
    public DbSet<NotificationSetting>  NotificationSettings  => Set<NotificationSetting>();

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

        // ── RefreshToken (unchanged) ────────────────────────────────────────
        builder.Entity<RefreshToken>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.HasIndex(e => e.TokenHash).IsUnique();
            entity.Property(e => e.TokenHash).HasMaxLength(64).IsRequired();
            entity.Property(e => e.UserId).HasMaxLength(450).IsRequired();
            entity.Property(e => e.DeviceInfo).HasMaxLength(200);
            entity.Property(e => e.IpAddress).HasMaxLength(45);
            entity.Property(e => e.ReplacedByHash).HasMaxLength(64);
            entity.HasOne(e => e.User).WithMany(u => u.RefreshTokens)
                  .HasForeignKey(e => e.UserId).OnDelete(DeleteBehavior.Cascade);
            entity.Ignore(e => e.IsExpired);
            entity.Ignore(e => e.IsRevoked);
            entity.Ignore(e => e.IsActive);
        });

        // ── UserInvitation (single-use, expiring first-login links) ─────────
        builder.Entity<UserInvitation>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.HasIndex(e => e.TokenHash).IsUnique();
            entity.Property(e => e.TokenHash).HasMaxLength(64).IsRequired();
            entity.Property(e => e.UserId).HasMaxLength(450).IsRequired();
            entity.Property(e => e.CreatedBy).HasMaxLength(450).IsRequired();
            entity.HasIndex(e => e.UserId);
            entity.HasOne(e => e.User).WithMany()
                  .HasForeignKey(e => e.UserId).OnDelete(DeleteBehavior.Cascade);
            entity.Ignore(e => e.IsExpired);
            entity.Ignore(e => e.IsUsed);
            entity.Ignore(e => e.IsRevoked);
            entity.Ignore(e => e.IsActive);
        });

        // ── AppUser (unchanged + new unique index on MemberId) ──────────────
        builder.Entity<AppUser>(entity =>
        {
            entity.Property(e => e.LanguagePreference).HasMaxLength(10).HasDefaultValue("en");
            // Nullable unique: one member ↔ one user account, but member can have no account
            entity.HasIndex(e => e.MemberId).IsUnique()
                  .HasFilter("\"MemberId\" IS NOT NULL");
        });

        // ── AppRole (unchanged) ─────────────────────────────────────────────
        builder.Entity<AppRole>(entity =>
        {
            entity.Property(e => e.Description).HasMaxLength(500);
        });

        // ── RolePermission (configurable RBAC matrix) ───────────────────────
        builder.Entity<RolePermission>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.Property(e => e.RoleId).HasMaxLength(450).IsRequired();
            entity.Property(e => e.Module).HasMaxLength(60).IsRequired();
            // One row per (role, module).
            entity.HasIndex(e => new { e.RoleId, e.Module }).IsUnique();
            entity.HasOne(e => e.Role).WithMany()
                  .HasForeignKey(e => e.RoleId).OnDelete(DeleteBehavior.Cascade);
        });

        // ── FamilyUnit ──────────────────────────────────────────────────────
        builder.Entity<FamilyUnit>(entity =>
        {
            entity.Property(e => e.FamilyName_en).HasMaxLength(200);
            entity.Property(e => e.FamilyName_zh).HasMaxLength(200);
        });

        // ── Member ──────────────────────────────────────────────────────────
        builder.Entity<Member>(entity =>
        {
            entity.HasQueryFilter(m => !m.IsDeleted);

            entity.Property(e => e.FirstName_en).HasMaxLength(100).IsRequired();
            entity.Property(e => e.LastName_en).HasMaxLength(100).IsRequired();
            entity.Property(e => e.NickName).HasMaxLength(100);
            entity.Property(e => e.FirstName_zh).HasMaxLength(100);
            entity.Property(e => e.LastName_zh).HasMaxLength(100);
            entity.Property(e => e.Entity).HasMaxLength(200);
            entity.Property(e => e.Gender).HasMaxLength(10);
            entity.Property(e => e.BaptismChurch).HasMaxLength(200);
            entity.Property(e => e.Email).HasMaxLength(200);
            entity.Property(e => e.PhoneCell).HasMaxLength(30);
            entity.Property(e => e.PhoneHome).HasMaxLength(30);
            entity.Property(e => e.Address).HasMaxLength(500);
            entity.Property(e => e.City).HasMaxLength(100);
            entity.Property(e => e.State).HasMaxLength(50);
            entity.Property(e => e.ZipCode).HasMaxLength(20);
            entity.Property(e => e.Country).HasMaxLength(100).HasDefaultValue("USA");
            entity.Property(e => e.PhotoBlobPath).HasMaxLength(500);
            entity.Property(e => e.Status).HasMaxLength(20).HasDefaultValue("Member");
            entity.Property(e => e.LanguagePreference).HasMaxLength(10).HasDefaultValue("en");
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.Property(e => e.DeletedBy).HasMaxLength(450);

            entity.HasIndex(e => e.Status).HasFilter("\"IsDeleted\" = false");
            entity.HasIndex(e => e.Email).HasFilter("\"Email\" IS NOT NULL");
            entity.HasIndex(e => e.FamilyUnitId);

            entity.HasOne(e => e.FamilyUnit).WithMany()
                  .HasForeignKey(e => e.FamilyUnitId).OnDelete(DeleteBehavior.SetNull);
        });

        // ── GivingCategory ───────────────────────────────────────────────────
        builder.Entity<GivingCategory>(entity =>
        {
            entity.Property(e => e.Name_en).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Name_zh).HasMaxLength(200);
            entity.Property(e => e.Description_en).HasMaxLength(500);
            entity.Property(e => e.Description_zh).HasMaxLength(500);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
        });

        // ── OfferingSession ──────────────────────────────────────────────────
        builder.Entity<OfferingSession>(entity =>
        {
            entity.Property(e => e.Status).HasMaxLength(20).HasDefaultValue("Draft");
            entity.Property(e => e.CashTotal).HasColumnType("decimal(18,2)");
            entity.Property(e => e.CheckTotal).HasColumnType("decimal(18,2)");
            entity.Property(e => e.SystemTotal).HasColumnType("decimal(18,2)");
            entity.Property(e => e.Difference).HasColumnType("decimal(18,2)");
            entity.Property(e => e.SubmittedBy).HasMaxLength(450);
            entity.Property(e => e.ReconciledBy).HasMaxLength(450);
            entity.Property(e => e.ProofPdfPath).HasMaxLength(500);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.HasIndex(e => e.SessionDate).IsUnique();
        });

        // ── Giving ───────────────────────────────────────────────────────────
        builder.Entity<Giving>(entity =>
        {
            entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
            entity.Property(e => e.PaymentMethod).HasMaxLength(20).IsRequired();
            entity.Property(e => e.CheckNumber).HasMaxLength(50);
            entity.Property(e => e.ZelleReferenceCode).HasMaxLength(100);
            entity.Property(e => e.PayPalTransactionId).HasMaxLength(100);
            entity.Property(e => e.Notes).HasMaxLength(500);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);

            entity.HasIndex(e => new { e.MemberId, e.GivingDate });
            entity.HasIndex(e => e.OfferingSessionId).HasFilter("\"OfferingSessionId\" IS NOT NULL");
            entity.HasIndex(e => e.GivingDate);

            entity.HasOne(e => e.GivingCategory).WithMany()
                  .HasForeignKey(e => e.GivingCategoryId).OnDelete(DeleteBehavior.Restrict);
            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull);
            entity.HasOne(e => e.OfferingSession).WithMany(s => s.Givings)
                  .HasForeignKey(e => e.OfferingSessionId).OnDelete(DeleteBehavior.Cascade);
        });

        // ── Ministry ─────────────────────────────────────────────────────────
        builder.Entity<Ministry>(entity =>
        {
            entity.Property(e => e.Name_en).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Name_zh).HasMaxLength(200);
            entity.Property(e => e.DefaultFunctionalClass).HasMaxLength(20).HasDefaultValue("Program");
        });

        // ── Form990ExpenseLine (Part IX natural-expense line catalog) ─────────
        builder.Entity<Form990ExpenseLine>(entity =>
        {
            entity.Property(e => e.LineCode).HasMaxLength(10).IsRequired();
            entity.Property(e => e.Name_en).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Name_zh).HasMaxLength(200);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.HasIndex(e => e.LineCode).IsUnique();
        });

        // ── Form1099Box (1099 reporting box catalog) ──────────────────────────
        builder.Entity<Form1099Box>(entity =>
        {
            entity.Property(e => e.BoxCode).HasMaxLength(10).IsRequired();
            entity.Property(e => e.Name_en).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Name_zh).HasMaxLength(200);
            entity.Property(e => e.FormType).HasMaxLength(20).IsRequired();
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.HasIndex(e => e.BoxCode).IsUnique();
        });

        // ── Payee1099 (1099 recipient master) ────────────────────────────────
        builder.Entity<Payee1099>(entity =>
        {
            entity.HasQueryFilter(p => !p.IsDeleted);
            entity.Property(e => e.LegalName).HasMaxLength(200).IsRequired();
            entity.Property(e => e.DisplayName).HasMaxLength(200);
            entity.Property(e => e.TaxClassification).HasMaxLength(40).IsRequired();
            entity.Property(e => e.TinType).HasMaxLength(10);
            entity.Property(e => e.TinLast4).HasMaxLength(4);
            entity.Property(e => e.State).HasMaxLength(2);
            entity.Property(e => e.Zip).HasMaxLength(10);
            entity.Property(e => e.W9Status).HasMaxLength(20).HasDefaultValue(Form1099.W9Status.Missing);
            entity.Property(e => e.AddressLine1).HasMaxLength(200);
            entity.Property(e => e.AddressLine2).HasMaxLength(200);
            entity.Property(e => e.City).HasMaxLength(100);
            entity.Property(e => e.Email).HasMaxLength(200);
            entity.Property(e => e.Phone).HasMaxLength(30);
            entity.Property(e => e.Notes).HasMaxLength(500);
            entity.Property(e => e.W9BlobPath).HasMaxLength(500);
            entity.Property(e => e.TinEncrypted).HasMaxLength(500);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.Property(e => e.DeletedBy).HasMaxLength(450);
            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull);
        });

        // ── ExpenseCategoryGroup ─────────────────────────────────────────────
        builder.Entity<ExpenseCategoryGroup>(entity =>
        {
            entity.Property(e => e.Name_en).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Name_zh).HasMaxLength(200);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.HasOne(e => e.Form990Line).WithMany()
                  .HasForeignKey(e => e.Form990LineId).OnDelete(DeleteBehavior.SetNull);
            entity.HasOne(e => e.Form1099Box).WithMany()
                  .HasForeignKey(e => e.Form1099BoxId).OnDelete(DeleteBehavior.SetNull);
        });

        // ── ExpenseSubCategory ───────────────────────────────────────────────
        builder.Entity<ExpenseSubCategory>(entity =>
        {
            entity.Property(e => e.Name_en).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Name_zh).HasMaxLength(200);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.HasOne(e => e.Group).WithMany(g => g.SubCategories)
                  .HasForeignKey(e => e.GroupId).OnDelete(DeleteBehavior.Restrict);
            entity.HasOne(e => e.Form990Line).WithMany()
                  .HasForeignKey(e => e.Form990LineId).OnDelete(DeleteBehavior.SetNull);
            entity.HasOne(e => e.Form1099Box).WithMany()
                  .HasForeignKey(e => e.Form1099BoxId).OnDelete(DeleteBehavior.SetNull);
        });

        // ── Expense ──────────────────────────────────────────────────────────
        builder.Entity<Expense>(entity =>
        {
            entity.HasQueryFilter(e => !e.IsDeleted);

            entity.Property(e => e.Type).HasMaxLength(30).IsRequired();
            entity.Property(e => e.Status).HasMaxLength(30).HasDefaultValue("Draft");
            entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
            entity.Property(e => e.Description).HasMaxLength(500).IsRequired();
            entity.Property(e => e.VendorName).HasMaxLength(200);
            entity.Property(e => e.CheckNumber).HasMaxLength(50);
            entity.Property(e => e.ReceiptBlobPath).HasMaxLength(500);
            entity.Property(e => e.ReviewNotes).HasMaxLength(500);
            entity.Property(e => e.SubmittedBy).HasMaxLength(450);
            entity.Property(e => e.ReviewedBy).HasMaxLength(450);
            entity.Property(e => e.PaidBy).HasMaxLength(450);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.Property(e => e.DeletedBy).HasMaxLength(450);

            entity.HasIndex(e => e.MinistryId);
            entity.HasIndex(e => e.Status).HasFilter("\"IsDeleted\" = false");
            entity.HasIndex(e => e.ExpenseDate);

            entity.HasOne(e => e.Ministry).WithMany()
                  .HasForeignKey(e => e.MinistryId).OnDelete(DeleteBehavior.Restrict);
            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull);
            entity.HasOne(e => e.Payee).WithMany()
                  .HasForeignKey(e => e.PayeeId).OnDelete(DeleteBehavior.SetNull);
        });

        // ── ExpenseLine (category breakdown of one Expense) ──────────────────
        builder.Entity<ExpenseLine>(entity =>
        {
            // Mirror the parent Expense's soft-delete filter (required relationship).
            entity.HasQueryFilter(l => !l.Expense!.IsDeleted);

            entity.Property(e => e.FunctionalClass).HasMaxLength(20);
            entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
            entity.Property(e => e.Description).HasMaxLength(500);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);

            entity.HasIndex(e => e.ExpenseId);

            entity.HasOne(e => e.Expense).WithMany(x => x.Lines)
                  .HasForeignKey(e => e.ExpenseId).OnDelete(DeleteBehavior.Cascade);
            entity.HasOne(e => e.CategoryGroup).WithMany()
                  .HasForeignKey(e => e.CategoryGroupId).OnDelete(DeleteBehavior.Restrict);
            entity.HasOne(e => e.SubCategory).WithMany()
                  .HasForeignKey(e => e.SubCategoryId).OnDelete(DeleteBehavior.Restrict);
        });

        // ── ExpenseSnapshot (reusable vendor-payment template) ───────────────
        builder.Entity<ExpenseSnapshot>(entity =>
        {
            entity.HasQueryFilter(s => !s.IsDeleted);

            entity.Property(e => e.Name).HasMaxLength(150).IsRequired();
            entity.Property(e => e.Description).HasMaxLength(500).IsRequired();
            entity.Property(e => e.VendorName).HasMaxLength(200);
            entity.Property(e => e.CheckNumber).HasMaxLength(50);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.Property(e => e.DeletedBy).HasMaxLength(450);

            entity.HasIndex(e => e.CreatedAt);

            entity.HasOne(e => e.Ministry).WithMany()
                  .HasForeignKey(e => e.MinistryId).OnDelete(DeleteBehavior.Restrict);
        });

        // ── ExpenseSnapshotLine (category breakdown of one snapshot) ─────────
        builder.Entity<ExpenseSnapshotLine>(entity =>
        {
            // Mirror the parent snapshot's soft-delete filter (required relationship).
            entity.HasQueryFilter(l => !l.Snapshot!.IsDeleted);

            entity.Property(e => e.FunctionalClass).HasMaxLength(20);
            entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
            entity.Property(e => e.Description).HasMaxLength(500);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);

            entity.HasIndex(e => e.SnapshotId);

            entity.HasOne(e => e.Snapshot).WithMany(x => x.Lines)
                  .HasForeignKey(e => e.SnapshotId).OnDelete(DeleteBehavior.Cascade);
            entity.HasOne(e => e.CategoryGroup).WithMany()
                  .HasForeignKey(e => e.CategoryGroupId).OnDelete(DeleteBehavior.Restrict);
            entity.HasOne(e => e.SubCategory).WithMany()
                  .HasForeignKey(e => e.SubCategoryId).OnDelete(DeleteBehavior.Restrict);
        });

        // ── ChurchProfile (singleton settings) ───────────────────────────────
        builder.Entity<ChurchProfile>(entity =>
        {
            entity.Property(e => e.Name).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Address).HasMaxLength(500);
            entity.Property(e => e.City).HasMaxLength(100);
            entity.Property(e => e.State).HasMaxLength(50);
            entity.Property(e => e.ZipCode).HasMaxLength(20);
            entity.Property(e => e.BankName).HasMaxLength(200);
            entity.Property(e => e.BankAccountNumber).HasMaxLength(50);
            entity.Property(e => e.BankRoutingNumber).HasMaxLength(50);
            entity.Property(e => e.PayerEin).HasMaxLength(20);
            entity.Property(e => e.NameZh).HasMaxLength(200);
            entity.Property(e => e.Phone).HasMaxLength(50);
            entity.Property(e => e.Email).HasMaxLength(200);
            entity.Property(e => e.Website).HasMaxLength(300);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.Property(e => e.AiProvider).HasMaxLength(20).HasDefaultValue("Claude");
            entity.Property(e => e.ClaudeModel).HasMaxLength(100).HasDefaultValue("claude-haiku-4-5-20251001");
            entity.Property(e => e.ClaudeApiKey).HasMaxLength(500);
            entity.Property(e => e.GeminiModel).HasMaxLength(100).HasDefaultValue("gemini-2.5-flash-lite");
            entity.Property(e => e.GeminiApiKey).HasMaxLength(500);
            // Optimistic-concurrency token for safe check-number allocation.
            entity.Property(e => e.xmin).IsRowVersion();
        });

        // ── SiteSetting (singleton presentation/locale settings) ─────────────
        builder.Entity<SiteSetting>(entity =>
        {
            entity.Property(e => e.SiteTitle).HasMaxLength(200).IsRequired();
            entity.Property(e => e.SiteTitleZh).HasMaxLength(200);
            entity.Property(e => e.DefaultLanguage).HasMaxLength(10).IsRequired();
            entity.Property(e => e.TimeZone).HasMaxLength(100).IsRequired();
            entity.Property(e => e.DateFormat).HasMaxLength(50).IsRequired();
            entity.Property(e => e.Currency).HasMaxLength(10).IsRequired();
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
        });

        // ── NotificationSetting (singleton SMTP + Line settings) ─────────────
        builder.Entity<NotificationSetting>(entity =>
        {
            entity.Property(e => e.SmtpHost).HasMaxLength(200);
            entity.Property(e => e.SmtpUser).HasMaxLength(200);
            entity.Property(e => e.SmtpPassword).HasMaxLength(500);
            entity.Property(e => e.FromAddress).HasMaxLength(200);
            entity.Property(e => e.FromName).HasMaxLength(200);
            entity.Property(e => e.LineChannelAccessToken).HasMaxLength(500);
            entity.Property(e => e.LineChannelSecret).HasMaxLength(200);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
        });

        // ── Check (disbursement) ─────────────────────────────────────────────
        builder.Entity<Check>(entity =>
        {
            entity.HasQueryFilter(c => !c.IsDeleted);

            entity.Property(e => e.CheckNumber).HasMaxLength(50).IsRequired();
            entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
            entity.Property(e => e.PayeeType).HasMaxLength(20).IsRequired();
            entity.Property(e => e.PayeeName).HasMaxLength(200).IsRequired();
            entity.Property(e => e.PayeeAddress).HasMaxLength(500);
            entity.Property(e => e.PayeeCity).HasMaxLength(100);
            entity.Property(e => e.PayeeState).HasMaxLength(50);
            entity.Property(e => e.PayeeZip).HasMaxLength(20);
            entity.Property(e => e.Status).HasMaxLength(20).HasDefaultValue("Issued");
            entity.Property(e => e.Memo).HasMaxLength(500);
            entity.Property(e => e.IssuedBy).HasMaxLength(450).IsRequired();
            entity.Property(e => e.VoidReason).HasMaxLength(500);
            entity.Property(e => e.VoidedBy).HasMaxLength(450);
            entity.Property(e => e.ReceiptSignatureBlobPath).HasMaxLength(500);
            entity.Property(e => e.ReceiptSignedName).HasMaxLength(200);
            entity.Property(e => e.ReceiptCapturedBy).HasMaxLength(450);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.Property(e => e.DeletedBy).HasMaxLength(450);

            // Unique check number among non-deleted rows.
            entity.HasIndex(e => e.CheckNumber).IsUnique().HasFilter("\"IsDeleted\" = false");
            entity.HasIndex(e => e.Status).HasFilter("\"IsDeleted\" = false");
            entity.HasIndex(e => e.CheckDate);

            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull);
        });

        // ── CheckLine ────────────────────────────────────────────────────────
        builder.Entity<CheckLine>(entity =>
        {
            // Mirror the parent Check's soft-delete filter (required relationship).
            entity.HasQueryFilter(l => !l.Check!.IsDeleted);

            entity.Property(e => e.Amount).HasColumnType("decimal(18,2)");
            entity.Property(e => e.Description).HasMaxLength(500).IsRequired();
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);

            entity.HasIndex(e => e.CheckId);
            entity.HasIndex(e => e.ExpenseId);

            entity.HasOne(e => e.Check).WithMany(c => c.Lines)
                  .HasForeignKey(e => e.CheckId).OnDelete(DeleteBehavior.Cascade);
            entity.HasOne(e => e.Expense).WithMany()
                  .HasForeignKey(e => e.ExpenseId).OnDelete(DeleteBehavior.Restrict);
        });

        // ── MealAttendance (one shared row per Sunday) ───────────────────────
        builder.Entity<MealAttendance>(entity =>
        {
            entity.Property(e => e.AdultCount).HasDefaultValue(0);
            entity.Property(e => e.YouthCount).HasDefaultValue(0);
            entity.Property(e => e.KidCount).HasDefaultValue(0);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.HasIndex(e => e.AttendanceDate).IsUnique();
        });

        // ── MonthlyStatement ─────────────────────────────────────────────────
        builder.Entity<MonthlyStatement>(entity =>
        {
            entity.Property(e => e.OpeningBalance).HasColumnType("decimal(18,2)");
            entity.Property(e => e.TotalGiving).HasColumnType("decimal(18,2)");
            entity.Property(e => e.TotalOtherIncome).HasColumnType("decimal(18,2)");
            entity.Property(e => e.TotalExpenses).HasColumnType("decimal(18,2)");
            entity.Property(e => e.CalculatedClosingBalance).HasColumnType("decimal(18,2)");
            entity.Property(e => e.BankStatementBalance).HasColumnType("decimal(18,2)");
            entity.Property(e => e.Difference).HasColumnType("decimal(18,2)");
            entity.Property(e => e.FinalizedBy).HasMaxLength(450);
            entity.Property(e => e.CreatedBy).HasMaxLength(450);
            entity.Property(e => e.UpdatedBy).HasMaxLength(450);
            entity.HasIndex(e => new { e.Year, e.Month }).IsUnique();
        });

        // ── Notifications (email + Line) ─────────────────────────────────────
        builder.Entity<MemberChannelBinding>(entity =>
        {
            entity.Property(e => e.Channel).HasMaxLength(20).IsRequired();
            entity.Property(e => e.ExternalId).HasMaxLength(100).IsRequired();
            entity.HasIndex(e => new { e.MemberId, e.Channel }).IsUnique();
            entity.HasIndex(e => new { e.Channel, e.ExternalId }).IsUnique();
            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<LineBindingCode>(entity =>
        {
            entity.Property(e => e.Code).HasMaxLength(20).IsRequired();
            entity.HasIndex(e => e.Code);
            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.Cascade);
        });

        builder.Entity<MessagingGroup>(entity =>
        {
            entity.Property(e => e.Channel).HasMaxLength(20).IsRequired();
            entity.Property(e => e.ExternalId).HasMaxLength(100).IsRequired();
            entity.Property(e => e.Name).HasMaxLength(200);
            entity.HasIndex(e => new { e.Channel, e.ExternalId }).IsUnique();
        });

        builder.Entity<NotificationLog>(entity =>
        {
            entity.Property(e => e.Channel).HasMaxLength(20).IsRequired();
            entity.Property(e => e.TargetType).HasMaxLength(20).IsRequired();
            entity.Property(e => e.TargetExternalId).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Subject).HasMaxLength(300);
            entity.Property(e => e.Status).HasMaxLength(20).IsRequired();
            entity.Property(e => e.SentByUserId).HasMaxLength(450).IsRequired();
            entity.HasIndex(e => e.SentAt);
            entity.HasIndex(e => e.Channel);
            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.SetNull);
            entity.HasOne(e => e.MessagingGroup).WithMany()
                  .HasForeignKey(e => e.MessagingGroupId).OnDelete(DeleteBehavior.SetNull);
        });

        builder.Entity<WebPushSubscription>(entity =>
        {
            entity.Property(e => e.Endpoint).HasMaxLength(2000).IsRequired();
            entity.Property(e => e.P256dh).HasMaxLength(200).IsRequired();
            entity.Property(e => e.Auth).HasMaxLength(100).IsRequired();
            entity.Property(e => e.UserAgent).HasMaxLength(400);
            entity.HasIndex(e => e.Endpoint).IsUnique();
            entity.HasIndex(e => e.MemberId);
            entity.HasOne(e => e.Member).WithMany()
                  .HasForeignKey(e => e.MemberId).OnDelete(DeleteBehavior.Cascade);
        });

        // ── SystemLog / AuditLog (append-only) ───────────────────────────────
        // Mapped here for SCHEMA only — there are deliberately no DbSets on this
        // context, so business code can't write logs through the audited context.
        // Runtime reads/writes go through the dedicated LogDbContext. Including
        // them in the model lets the single startup migration create the tables.
        LogModelConfiguration.Configure(builder);
    }
}
