using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using TakeoutSaaS.Domain.Membership.Entities;
using TakeoutSaaS.Domain.Merchants.Entities;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Infrastructure.Common.Persistence;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Security;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Infrastructure.Logs.Persistence;

/// <summary>
/// 日志库 DbContext。
/// </summary>
public sealed class TakeoutLogsDbContext(
    DbContextOptions<TakeoutLogsDbContext> options,
    ITenantProvider tenantProvider,
    ICurrentUserAccessor? currentUserAccessor = null,
    IIdGenerator? idGenerator = null)
    : TenantAwareDbContext(options, tenantProvider, currentUserAccessor, idGenerator)
{
    /// <summary>
    /// 租户审计日志集合。
    /// </summary>
    public DbSet<TenantAuditLog> TenantAuditLogs => Set<TenantAuditLog>();

    /// <summary>
    /// 商户审计日志集合。
    /// </summary>
    public DbSet<MerchantAuditLog> MerchantAuditLogs => Set<MerchantAuditLog>();

    /// <summary>
    /// 商户变更日志集合。
    /// </summary>
    public DbSet<MerchantChangeLog> MerchantChangeLogs => Set<MerchantChangeLog>();

    /// <summary>
    /// 运营操作日志集合。
    /// </summary>
    public DbSet<OperationLog> OperationLogs => Set<OperationLog>();

    /// <summary>
    /// 操作日志消息去重集合。
    /// </summary>
    public DbSet<OperationLogInboxMessage> OperationLogInboxMessages => Set<OperationLogInboxMessage>();

    /// <summary>
    /// 成长值日志集合。
    /// </summary>
    public DbSet<MemberGrowthLog> MemberGrowthLogs => Set<MemberGrowthLog>();

    /// <summary>
    /// 配置实体模型。
    /// </summary>
    /// <param name="modelBuilder">模型构建器。</param>
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        ConfigureTenantAuditLog(modelBuilder.Entity<TenantAuditLog>());
        ConfigureMerchantAuditLog(modelBuilder.Entity<MerchantAuditLog>());
        ConfigureMerchantChangeLog(modelBuilder.Entity<MerchantChangeLog>());
        ConfigureOperationLog(modelBuilder.Entity<OperationLog>());
        ConfigureOperationLogInboxMessage(modelBuilder.Entity<OperationLogInboxMessage>());
        ConfigureMemberGrowthLog(modelBuilder.Entity<MemberGrowthLog>());
        ApplyTenantQueryFilters(modelBuilder);
    }

    private static void ConfigureTenantAuditLog(EntityTypeBuilder<TenantAuditLog> builder)
    {
        builder.ToTable("tenant_audit_logs");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.TenantId).IsRequired();
        builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
        builder.Property(x => x.Description).HasMaxLength(1024);
        builder.Property(x => x.OperatorName).HasMaxLength(64);
        builder.HasIndex(x => x.TenantId);
    }

    private static void ConfigureMerchantAuditLog(EntityTypeBuilder<MerchantAuditLog> builder)
    {
        builder.ToTable("merchant_audit_logs");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.MerchantId).IsRequired();
        builder.Property(x => x.Action).HasConversion<int>().IsRequired();
        builder.Property(x => x.Title).HasMaxLength(200).IsRequired();
        builder.Property(x => x.Description).HasMaxLength(1024);
        builder.Property(x => x.OperatorName).HasMaxLength(100);
        builder.Property(x => x.IpAddress).HasMaxLength(50);
        builder.HasIndex(x => new { x.TenantId, x.MerchantId });
        builder.HasIndex(x => new { x.MerchantId, x.CreatedAt });
        builder.HasIndex(x => new { x.TenantId, x.CreatedAt });
    }

    private static void ConfigureMerchantChangeLog(EntityTypeBuilder<MerchantChangeLog> builder)
    {
        builder.ToTable("merchant_change_logs");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.MerchantId).IsRequired();
        builder.Property(x => x.FieldName).HasMaxLength(100).IsRequired();
        builder.Property(x => x.OldValue).HasColumnType("text");
        builder.Property(x => x.NewValue).HasColumnType("text");
        builder.Property(x => x.ChangeType).HasMaxLength(20).IsRequired();
        builder.Property(x => x.ChangedByName).HasMaxLength(100);
        builder.Property(x => x.ChangeReason).HasMaxLength(512);
        builder.HasIndex(x => new { x.MerchantId, x.CreatedAt });
        builder.HasIndex(x => new { x.TenantId, x.CreatedAt });
    }

    private static void ConfigureOperationLog(EntityTypeBuilder<OperationLog> builder)
    {
        builder.ToTable("operation_logs");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.OperationType).HasMaxLength(64).IsRequired();
        builder.Property(x => x.TargetType).HasMaxLength(64).IsRequired();
        builder.Property(x => x.TargetIds).HasColumnType("text");
        builder.Property(x => x.OperatorId).HasMaxLength(64);
        builder.Property(x => x.OperatorName).HasMaxLength(128);
        builder.Property(x => x.Parameters).HasColumnType("text");
        builder.Property(x => x.Result).HasColumnType("text");
        builder.Property(x => x.Success).IsRequired();
        builder.HasIndex(x => new { x.OperationType, x.CreatedAt });
        builder.HasIndex(x => x.CreatedAt);
    }

    private static void ConfigureOperationLogInboxMessage(EntityTypeBuilder<OperationLogInboxMessage> builder)
    {
        builder.ToTable("operation_log_inbox_messages");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.MessageId).IsRequired();
        builder.Property(x => x.ConsumedAt).IsRequired();
        builder.HasIndex(x => x.MessageId).IsUnique();
    }

    private static void ConfigureMemberGrowthLog(EntityTypeBuilder<MemberGrowthLog> builder)
    {
        builder.ToTable("member_growth_logs");
        builder.HasKey(x => x.Id);
        builder.Property(x => x.MemberId).IsRequired();
        builder.Property(x => x.Notes).HasMaxLength(256);
        builder.HasIndex(x => new { x.TenantId, x.MemberId, x.OccurredAt });
    }
}
