using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using TakeoutSaaS.Domain.Stores.Entities;
using TakeoutSaaS.Infrastructure.Common.Persistence;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Security;

namespace TakeoutSaaS.Infrastructure.App.Persistence;

/// <summary>
/// 租户端门店模块业务 DbContext。
/// </summary>
public sealed class TakeoutTenantAppDbContext(
    DbContextOptions<TakeoutTenantAppDbContext> options,
    ICurrentUserAccessor? currentUserAccessor = null,
    IIdGenerator? idGenerator = null)
    : AppDbContext(options, currentUserAccessor, idGenerator)
{
    /// <summary>
    /// 门店实体集合。
    /// </summary>
    public DbSet<Store> Stores => Set<Store>();

    /// <summary>
    /// 门店费用集合。
    /// </summary>
    public DbSet<StoreFee> StoreFees => Set<StoreFee>();

    /// <summary>
    /// 门店资质集合。
    /// </summary>
    public DbSet<StoreQualification> StoreQualifications => Set<StoreQualification>();

    /// <summary>
    /// 门店审核记录集合。
    /// </summary>
    public DbSet<StoreAuditRecord> StoreAuditRecords => Set<StoreAuditRecord>();

    /// <summary>
    /// 门店营业时段集合。
    /// </summary>
    public DbSet<StoreBusinessHour> StoreBusinessHours => Set<StoreBusinessHour>();

    /// <summary>
    /// 门店节假日集合。
    /// </summary>
    public DbSet<StoreHoliday> StoreHolidays => Set<StoreHoliday>();

    /// <summary>
    /// 门店配送区域集合。
    /// </summary>
    public DbSet<StoreDeliveryZone> StoreDeliveryZones => Set<StoreDeliveryZone>();

    /// <summary>
    /// 门店桌台区域集合。
    /// </summary>
    public DbSet<StoreTableArea> StoreTableAreas => Set<StoreTableArea>();

    /// <summary>
    /// 门店桌台集合。
    /// </summary>
    public DbSet<StoreTable> StoreTables => Set<StoreTable>();

    /// <summary>
    /// 门店员工排班集合。
    /// </summary>
    public DbSet<StoreEmployeeShift> StoreEmployeeShifts => Set<StoreEmployeeShift>();

    /// <summary>
    /// 门店自提配置集合。
    /// </summary>
    public DbSet<StorePickupSetting> StorePickupSettings => Set<StorePickupSetting>();

    /// <summary>
    /// 门店自提时段集合。
    /// </summary>
    public DbSet<StorePickupSlot> StorePickupSlots => Set<StorePickupSlot>();

    /// <inheritdoc />
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 1. 先应用基类配置（软删除、审计、注释）
        base.OnModelCreating(modelBuilder);

        // 2. 配置门店模块映射
        ConfigureModel(modelBuilder);
    }

    private static void ConfigureModel(ModelBuilder modelBuilder)
    {
        ConfigureStore(modelBuilder.Entity<Store>());
        ConfigureStoreFee(modelBuilder.Entity<StoreFee>());
        ConfigureStoreQualification(modelBuilder.Entity<StoreQualification>());
        ConfigureStoreAuditRecord(modelBuilder.Entity<StoreAuditRecord>());
        ConfigureStoreBusinessHour(modelBuilder.Entity<StoreBusinessHour>());
        ConfigureStoreHoliday(modelBuilder.Entity<StoreHoliday>());
        ConfigureStoreDeliveryZone(modelBuilder.Entity<StoreDeliveryZone>());
        ConfigureStoreTableArea(modelBuilder.Entity<StoreTableArea>());
        ConfigureStoreTable(modelBuilder.Entity<StoreTable>());
        ConfigureStoreEmployeeShift(modelBuilder.Entity<StoreEmployeeShift>());
        ConfigureStorePickupSetting(modelBuilder.Entity<StorePickupSetting>());
        ConfigureStorePickupSlot(modelBuilder.Entity<StorePickupSlot>());
    }

    private static void ConfigureStore(EntityTypeBuilder<Store> builder)
    {
        builder.ToTable("stores");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Code).HasMaxLength(32).IsRequired();
        builder.Property(x => x.Name).HasMaxLength(128).IsRequired();
        builder.Property(x => x.Phone).HasMaxLength(32);
        builder.Property(x => x.ManagerName).HasMaxLength(64);
        builder.Property(x => x.BusinessLicenseNumber).HasMaxLength(50);
        builder.Property(x => x.LegalRepresentative).HasMaxLength(100);
        builder.Property(x => x.RegisteredAddress).HasMaxLength(500);
        builder.Property(x => x.BusinessLicenseImageUrl).HasMaxLength(500);
        builder.Property(x => x.SignboardImageUrl).HasMaxLength(500);
        builder.Property(x => x.OwnershipType).HasConversion<int>();
        builder.Property(x => x.AuditStatus).HasConversion<int>();
        builder.Property(x => x.BusinessStatus).HasConversion<int>();
        builder.Property(x => x.ClosureReason).HasConversion<int?>();
        builder.Property(x => x.ClosureReasonText).HasMaxLength(500);
        builder.Property(x => x.RejectionReason).HasMaxLength(500);
        builder.Property(x => x.ForceCloseReason).HasMaxLength(500);
        builder.Property(x => x.Province).HasMaxLength(64);
        builder.Property(x => x.City).HasMaxLength(64);
        builder.Property(x => x.District).HasMaxLength(64);
        builder.Property(x => x.Address).HasMaxLength(256);
        builder.Property(x => x.BusinessHours).HasMaxLength(256);
        builder.Property(x => x.Announcement).HasMaxLength(512);
        builder.Property(x => x.DeliveryRadiusKm).HasPrecision(6, 2);
        builder.HasIndex(x => new { x.TenantId, x.MerchantId });
        builder.HasIndex(x => new { x.TenantId, x.Code }).IsUnique();
        builder.HasIndex(x => new { x.TenantId, x.AuditStatus });
        builder.HasIndex(x => new { x.TenantId, x.BusinessStatus });
        builder.HasIndex(x => new { x.TenantId, x.OwnershipType });
        builder.HasIndex(x => new { x.Longitude, x.Latitude })
            .HasFilter("\"Longitude\" IS NOT NULL AND \"Latitude\" IS NOT NULL");
        builder.HasIndex(x => new { x.MerchantId, x.BusinessLicenseNumber })
            .IsUnique()
            .HasFilter("\"BusinessLicenseNumber\" IS NOT NULL AND \"Status\" <> 3");
    }

    private static void ConfigureStoreFee(EntityTypeBuilder<StoreFee> builder)
    {
        builder.ToTable("store_fees");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.MinimumOrderAmount).HasPrecision(10, 2);
        builder.Property(x => x.BaseDeliveryFee).HasPrecision(10, 2);
        builder.Property(x => x.PackagingFeeMode).HasConversion<int>();
        builder.Property(x => x.OrderPackagingFeeMode).HasConversion<int>();
        builder.Property(x => x.FixedPackagingFee).HasPrecision(10, 2);
        builder.Property(x => x.PackagingFeeTiersJson).HasColumnType("text");
        builder.Property(x => x.FreeDeliveryThreshold).HasPrecision(10, 2);
        builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
        builder.HasIndex(x => x.TenantId);
    }

    private static void ConfigureStoreQualification(EntityTypeBuilder<StoreQualification> builder)
    {
        builder.ToTable("store_qualifications");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.QualificationType).HasConversion<int>();
        builder.Property(x => x.FileUrl).HasMaxLength(500).IsRequired();
        builder.Property(x => x.DocumentNumber).HasMaxLength(100);
        builder.Property(x => x.IssuedAt).HasColumnType("date");
        builder.Property(x => x.ExpiresAt).HasColumnType("date");
        builder.Property(x => x.SortOrder).HasDefaultValue(100);
        builder.HasIndex(x => new { x.TenantId, x.StoreId });
        builder.HasIndex(x => x.ExpiresAt).HasFilter("\"ExpiresAt\" IS NOT NULL");
        builder.Ignore(x => x.IsExpired);
        builder.Ignore(x => x.IsExpiringSoon);
    }

    private static void ConfigureStoreAuditRecord(EntityTypeBuilder<StoreAuditRecord> builder)
    {
        builder.ToTable("store_audit_records");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.Action).HasConversion<int>();
        builder.Property(x => x.PreviousStatus).HasConversion<int?>();
        builder.Property(x => x.NewStatus).HasConversion<int>();
        builder.Property(x => x.OperatorName).HasMaxLength(100).IsRequired();
        builder.Property(x => x.RejectionReason).HasMaxLength(500);
        builder.Property(x => x.Remarks).HasMaxLength(1000);
        builder.HasIndex(x => new { x.TenantId, x.StoreId });
        builder.HasIndex(x => x.CreatedAt);
    }

    private static void ConfigureStoreBusinessHour(EntityTypeBuilder<StoreBusinessHour> builder)
    {
        builder.ToTable("store_business_hours");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.DayOfWeek).HasConversion<int>();
        builder.Property(x => x.HourType).HasConversion<int>();
        builder.Property(x => x.StartTime).HasColumnType("time");
        builder.Property(x => x.EndTime).HasColumnType("time");
        builder.Property(x => x.CapacityLimit);
        builder.Property(x => x.Notes).HasMaxLength(256);
        builder.HasIndex(x => new { x.TenantId, x.StoreId, x.DayOfWeek });
    }

    private static void ConfigureStoreHoliday(EntityTypeBuilder<StoreHoliday> builder)
    {
        builder.ToTable("store_holidays");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.Date).HasColumnType("date");
        builder.Property(x => x.EndDate).HasColumnType("date");
        builder.Property(x => x.IsAllDay).HasDefaultValue(true);
        builder.Property(x => x.StartTime).HasColumnType("time");
        builder.Property(x => x.EndTime).HasColumnType("time");
        builder.Property(x => x.OverrideType).HasConversion<int>();
        builder.Property(x => x.IsClosed).HasDefaultValue(true);
        builder.Property(x => x.Reason).HasMaxLength(256);
        builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Date });
    }

    private static void ConfigureStoreDeliveryZone(EntityTypeBuilder<StoreDeliveryZone> builder)
    {
        builder.ToTable("store_delivery_zones");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.ZoneName).HasMaxLength(128).IsRequired();
        builder.Property(x => x.PolygonGeoJson).HasColumnType("text").IsRequired();
        builder.Property(x => x.MinimumOrderAmount).HasPrecision(10, 2);
        builder.Property(x => x.DeliveryFee).HasPrecision(10, 2);
        builder.Property(x => x.EstimatedMinutes).IsRequired();
        builder.Property(x => x.SortOrder).HasDefaultValue(100);
        builder.HasIndex(x => new { x.TenantId, x.StoreId });
    }

    private static void ConfigureStoreTableArea(EntityTypeBuilder<StoreTableArea> builder)
    {
        builder.ToTable("store_table_areas");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
        builder.Property(x => x.Description).HasMaxLength(256);
        builder.Property(x => x.SortOrder).HasDefaultValue(100);
        builder.HasIndex(x => new { x.TenantId, x.StoreId });
        builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name }).IsUnique();
    }

    private static void ConfigureStoreTable(EntityTypeBuilder<StoreTable> builder)
    {
        builder.ToTable("store_tables");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.AreaId).IsRequired();
        builder.Property(x => x.TableCode).HasMaxLength(32).IsRequired();
        builder.Property(x => x.Capacity).HasDefaultValue(2);
        builder.Property(x => x.Tags).HasMaxLength(256);
        builder.Property(x => x.Status).HasConversion<int>();
        builder.Property(x => x.QrCodeUrl).HasMaxLength(512);
        builder.HasIndex(x => new { x.TenantId, x.StoreId, x.TableCode }).IsUnique();
        builder.HasIndex(x => new { x.TenantId, x.StoreId, x.AreaId });
    }

    private static void ConfigureStoreEmployeeShift(EntityTypeBuilder<StoreEmployeeShift> builder)
    {
        builder.ToTable("store_employee_shifts");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.StaffId).IsRequired();
        builder.Property(x => x.ShiftDate).HasColumnType("date");
        builder.Property(x => x.StartTime).HasColumnType("time");
        builder.Property(x => x.EndTime).HasColumnType("time");
        builder.Property(x => x.RoleType).HasConversion<int>();
        builder.Property(x => x.Notes).HasMaxLength(256);
        builder.HasIndex(x => new { x.TenantId, x.StoreId, x.ShiftDate });
    }

    private static void ConfigureStorePickupSetting(EntityTypeBuilder<StorePickupSetting> builder)
    {
        builder.ToTable("store_pickup_settings");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.AllowToday).HasDefaultValue(true);
        builder.Property(x => x.AllowDaysAhead).HasDefaultValue(3);
        builder.Property(x => x.DefaultCutoffMinutes).HasDefaultValue(30);
        builder.Property(x => x.MaxQuantityPerOrder);
        builder.Property(x => x.RowVersion)
            .IsRequired()
            .IsConcurrencyToken()
            .ValueGeneratedNever();
        builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
    }

    private static void ConfigureStorePickupSlot(EntityTypeBuilder<StorePickupSlot> builder)
    {
        builder.ToTable("store_pickup_slots");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.StoreId).IsRequired();
        builder.Property(x => x.Name).HasMaxLength(64).IsRequired();
        builder.Property(x => x.StartTime).HasColumnType("time");
        builder.Property(x => x.EndTime).HasColumnType("time");
        builder.Property(x => x.CutoffMinutes).HasDefaultValue(30);
        builder.Property(x => x.Capacity).HasDefaultValue(0);
        builder.Property(x => x.ReservedCount).HasDefaultValue(0);
        builder.Property(x => x.Weekdays).HasMaxLength(32).HasDefaultValue("1,2,3,4,5,6,7");
        builder.Property(x => x.IsEnabled).HasDefaultValue(true);
        builder.Property(x => x.RowVersion)
            .IsRequired()
            .IsConcurrencyToken()
            .ValueGeneratedNever();
        builder.HasIndex(x => new { x.TenantId, x.StoreId, x.StartTime, x.EndTime }).IsUnique();
    }
}
