using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Reflection;
using TakeoutSaaS.Shared.Abstractions.Entities;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Security;

namespace TakeoutSaaS.Infrastructure.Common.Persistence;

/// <summary>
/// 应用基础 DbContext，统一处理审计字段、软删除与全局查询过滤。
/// </summary>
public abstract class AppDbContext(
    DbContextOptions options,
    ICurrentUserAccessor? currentUserAccessor = null,
    IIdGenerator? idGenerator = null) : DbContext(options)
{
    private readonly ICurrentUserAccessor? _currentUserAccessor = currentUserAccessor;
    private readonly IIdGenerator? _idGenerator = idGenerator;

    /// <summary>
    /// 构建模型时应用软删除过滤器。
    /// </summary>
    /// <param name="modelBuilder">模型构建器。</param>
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        ApplySoftDeleteQueryFilters(modelBuilder);
        modelBuilder.ApplyXmlComments();
    }

    /// <summary>
    /// 保存更改前应用元数据填充。
    /// </summary>
    /// <returns>受影响行数。</returns>
    public override int SaveChanges()
    {
        OnBeforeSaving();
        return base.SaveChanges();
    }

    /// <summary>
    /// 异步保存更改前应用元数据填充。
    /// </summary>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>受影响行数。</returns>
    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        OnBeforeSaving();
        return base.SaveChangesAsync(cancellationToken);
    }

    /// <summary>
    /// 保存前处理审计、软删除等元数据，可在子类中扩展。
    /// </summary>
    protected virtual void OnBeforeSaving()
    {
        ApplyIdGeneration();
        ApplySoftDeleteMetadata();
        ApplyAuditMetadata();
    }

    /// <summary>
    /// 为新增实体生成雪花 ID。
    /// </summary>
    private void ApplyIdGeneration()
    {
        if (_idGenerator == null)
        {
            return;
        }

        foreach (var entry in ChangeTracker.Entries<EntityBase>())
        {
            if (entry.State != EntityState.Added)
            {
                continue;
            }

            if (entry.Entity.Id == 0)
            {
                entry.Entity.Id = _idGenerator.NextId();
            }
        }
    }

    /// <summary>
    /// 将软删除实体的删除操作转换为设置 DeletedAt。
    /// </summary>
    private void ApplySoftDeleteMetadata()
    {
        var utcNow = DateTime.UtcNow;
        var actor = GetCurrentUserIdOrNull();
        foreach (var entry in ChangeTracker.Entries<ISoftDeleteEntity>())
        {
            if (entry.State == EntityState.Added && entry.Entity.DeletedAt.HasValue)
            {
                entry.Entity.DeletedAt = null;
            }

            if (entry.State != EntityState.Deleted)
            {
                continue;
            }

            entry.State = EntityState.Modified;
            entry.Entity.DeletedAt = utcNow;
            if (entry.Entity is IAuditableEntity auditable)
            {
                auditable.DeletedBy = actor;
                if (!auditable.UpdatedAt.HasValue)
                {
                    auditable.UpdatedAt = utcNow;
                    auditable.UpdatedBy = actor;
                }
            }
        }
    }

    /// <summary>
    /// 对审计实体填充创建与更新时间。
    /// </summary>
    private void ApplyAuditMetadata()
    {
        var utcNow = DateTime.UtcNow;
        var actor = GetCurrentUserIdOrNull();

        foreach (var entry in ChangeTracker.Entries<IAuditableEntity>())
        {
            if (entry.State == EntityState.Added)
            {
                entry.Entity.CreatedAt = utcNow;
                entry.Entity.UpdatedAt = null;
                entry.Entity.CreatedBy ??= actor;
                entry.Entity.UpdatedBy = null;
                entry.Entity.DeletedBy = null;
                entry.Entity.DeletedAt = null;
            }
            else if (entry.State == EntityState.Modified)
            {
                entry.Entity.UpdatedAt = utcNow;
                entry.Entity.UpdatedBy = actor;
            }
        }
    }

    private long? GetCurrentUserIdOrNull()
    {
        var userId = _currentUserAccessor?.UserId ?? 0;
        return userId == 0 ? null : userId;
    }

    /// <summary>
    /// 应用软删除查询过滤器，自动排除 DeletedAt 不为 null 的记录。
    /// </summary>
    /// <param name="modelBuilder">模型构建器。</param>
    protected void ApplySoftDeleteQueryFilters(ModelBuilder modelBuilder)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            if (!typeof(ISoftDeleteEntity).IsAssignableFrom(entityType.ClrType))
            {
                continue;
            }

            var methodInfo = typeof(AppDbContext)
                .GetMethod(nameof(SetSoftDeleteFilter), BindingFlags.Instance | BindingFlags.NonPublic)!
                .MakeGenericMethod(entityType.ClrType);

            methodInfo.Invoke(this, new object[] { modelBuilder });
        }
    }

    /// <summary>
    /// 设置软删除查询过滤器。
    /// </summary>
    /// <typeparam name="TEntity">实体类型。</typeparam>
    /// <param name="modelBuilder">模型构建器。</param>
    private void SetSoftDeleteFilter<TEntity>(ModelBuilder modelBuilder)
        where TEntity : class, ISoftDeleteEntity
    {
        modelBuilder.Entity<TEntity>().HasQueryFilter(entity => entity.DeletedAt == null);
    }

    /// <summary>
    /// 配置审计字段的通用约束。
    /// </summary>
    /// <typeparam name="TEntity">实体类型。</typeparam>
    /// <param name="builder">实体构建器。</param>
    protected static void ConfigureAuditableEntity<TEntity>(EntityTypeBuilder<TEntity> builder)
        where TEntity : class, IAuditableEntity
    {
        builder.Property(x => x.CreatedAt).IsRequired();
        builder.Property(x => x.UpdatedAt);
        builder.Property(x => x.DeletedAt);
        builder.Property(x => x.CreatedBy);
        builder.Property(x => x.UpdatedBy);
        builder.Property(x => x.DeletedBy);
    }

    /// <summary>
    /// 配置软删除字段的通用约束。
    /// </summary>
    /// <typeparam name="TEntity">实体类型。</typeparam>
    /// <param name="builder">实体构建器。</param>
    protected static void ConfigureSoftDeleteEntity<TEntity>(EntityTypeBuilder<TEntity> builder)
        where TEntity : class, ISoftDeleteEntity
    {
        builder.Property(x => x.DeletedAt);
    }
}
