using EntityFramework.Exceptions.Common;
using IRaCIS.Core.Domain.BaseModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore.Common;
using MassTransit;
using MassTransit.Mediator;
using MassTransit.Transports;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using System.Data;


namespace IRaCIS.Core.Infra.EFCore;
/// <summary>
/// ef 官方文档 :https://learn.microsoft.com/zh-cn/ef/core/logging-events-diagnostics/interceptors
/// </summary>
/// <param name="_userInfo"></param>
/// <param name="_logger"></param>
/// <param name="_mediator"></param>
public class AuditEntityInterceptor(IUserInfo _userInfo,
    ILogger<AuditEntityInterceptor> _logger
    , IMediator _mediator
    ) : SaveChangesInterceptor
{

    /// <summary>
    /// 在事务提交之前执行
    /// </summary>
    /// <param name="eventData"></param>
    /// <param name="result"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData,
        InterceptionResult<int> result, CancellationToken cancellationToken = default)
    {
        //////领域命令 （同一个事务提交的一些逻辑，类似Trigger 保存事务之前执行的一些逻辑）  IMediator 和autofac 有冲突，不在一个事务，废弃。
        //eventData.Context.AddDomainCommands();

        //领域事件
        eventData.Context.AddDomainEvents();

        //审计时间
        AuditEntities(eventData.Context);

        //IRC稽查  放在savechange 之前 不影响之前的逻辑
        IRCDataInspection(eventData.Context);

        //return result;

        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }
    public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
    {

        //领域事件
        eventData.Context.AddDomainEvents();

        //审计时间
        AuditEntities(eventData.Context);

        //IRC稽查  放在savechange 之前 不影响之前的逻辑
        IRCDataInspection(eventData.Context);

        //return result;

        return base.SavingChanges(eventData, result);
    }
    public void AuditEntities(DbContext? context)
    {
        if (context == null) return;

        // 获取所有更改，删除，新增的实体，但排除审计实体（避免死循环）
        foreach (var entry in context.ChangeTracker.Entries().Where(u => (u.State == EntityState.Modified || u.State == EntityState.Added))
                                                             //.Where(x => !typeof(DataInspection).IsAssignableFrom(x.Entity.GetType()))
                                                             .ToList())
        {
            // 检查属性是否为string类型，并且值为null
            foreach (var property in entry.Properties.Where(t => t.Metadata.ClrType == typeof(string) && t.CurrentValue == null))
            {
                property.CurrentValue = string.Empty;
            }

            switch (entry.State)
            {

                case EntityState.Deleted:

                    break;
                case EntityState.Modified:

                    if (entry.Entity is IAuditUpdate updateEntity1)
                    {
                        updateEntity1.UpdateTime = DateTime.Now;
                        updateEntity1.UpdateUserId = _userInfo.Id;
                    }

                    if (entry.Entity is ISoftDelete softDelete)
                    {
                        if (softDelete.IsDeleted)
                        {
                            softDelete.DeleteUserId = _userInfo.Id;
                            softDelete.DeletedTime = DateTime.Now;
                        }
                        else
                        {
                            softDelete.DeletedTime = null;
                        }
                    }

                    break;
                //添加的时候，更新审计字段也赋值
                case EntityState.Added:


                    if (entry.Entity is IAuditAdd addEntity)
                    {
                        if (addEntity.CreateTime == default(DateTime))
                        {
                            addEntity.CreateTime = DateTime.Now;
                        }
                        if (addEntity.CreateUserId == default(Guid))
                        {
                            addEntity.CreateUserId = _userInfo.Id;
                        }

                    }

                    if (entry.Entity is IAuditUpdate updateEntity)
                    {
                        updateEntity.UpdateTime = DateTime.Now;
                        updateEntity.UpdateUserId = _userInfo.Id;
                    }
                    break;
            }
        }
    }

    public void IRCDataInspection(DbContext? context)
    {
        //触发器里面提交事务 业务方法里面提交事务 会记录两次
        var inspectionGeneralIdList = context.ChangeTracker.Entries().Where(t => typeof(DataInspection).IsAssignableFrom(t.Entity.GetType())).Select(t => ((DataInspection)t.Entity).GeneralId).ToList();

        var entities = context.ChangeTracker.Entries().Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added))
            .Where(t => !typeof(DataInspection).IsAssignableFrom(t.Entity.GetType()) && !inspectionGeneralIdList.Contains(((Entity)t.Entity).Id))
            .ToList();
        AuditingData auditingData = new AuditingData((IRaCISDBContext)context, _userInfo);


        if (entities.Count > 0)
        {
            auditingData.InsertAddEntitys(entities).GetAwaiter().GetResult();

        }
    }


    #region 异常处理


    private void LoggerDBContextException(DbContextErrorEventData eventData)
    {
        var ex = eventData.Exception;

        if (ex is UniqueConstraintException)
        {
            _logger.LogError("该唯一键已经存在于数据库中:" + ex.InnerException?.Message ?? ex.Message);
        }
        else if (ex is TimeoutException)
        {
            _logger.LogError("数据库操作已经超时，请稍后重试:" + ex.InnerException?.Message ?? ex.Message);
        }
        else if (ex is CannotInsertNullException)
        {
            _logger.LogError("无法在非空列上插入空值:" + ex.InnerException is null ? ex.Message : ex.InnerException?.Message);
        }
        else if (ex is MaxLengthExceededException)
        {
            _logger.LogError("字符串超过了数据库列的最大长度:" + ex.InnerException is null ? ex.Message : ex.InnerException?.Message);
        }
        else if (ex is NumericOverflowException)
        {
            _logger.LogError("数值超过了数据类型的范围:" + ex.InnerException is null ? ex.Message : ex.InnerException?.Message);
        }
        else if (ex is SyntaxErrorException)
        {
            _logger.LogError("SQL 查询中存在语法错误:" + ex.InnerException is null ? ex.Message : ex.InnerException?.Message);
        }
        else if (ex is ReferenceConstraintException)
        {
            _logger.LogError("无法进行当前操作，当前数据不符合外键约束:" + ex.InnerException is null ? ex.Message : ex.InnerException?.Message);
        }
        else if (ex is DbUpdateConcurrencyException)
        {
            _logger.LogError("并发更新异常:" + ex.InnerException is null ? ex.Message : ex.InnerException?.Message);
        }
        else
        {
            _logger.LogError("SQL 事务失败,请检查环境:" + ex.InnerException is null ? ex.Message : ex.InnerException?.Message);
        }
    }

    //在保存失败后处理异常并记录日志
    public override Task SaveChangesFailedAsync(DbContextErrorEventData eventData, CancellationToken cancellationToken = default)
    {

        LoggerDBContextException(eventData);


        return base.SaveChangesFailedAsync(eventData, cancellationToken);
    }

    public override void SaveChangesFailed(DbContextErrorEventData eventData)
    {
        LoggerDBContextException(eventData);

        base.SaveChangesFailed(eventData);
    }

    #endregion

    #region 领域命令分发

    private async Task DispatchDomainCommands(DbContext? context)
    {
        if (context == null) return;

        var entities = context.ChangeTracker
            .Entries<Entity>()
            .Where(e => e.Entity.DomainCommands.Any())
            .Select(e => e.Entity)
            .ToList();

        var domainCommands = entities
            .SelectMany(e => e.DomainCommands)
            .ToList();

        entities.ForEach(e => e.ClearDomainCommands());

        foreach (var domainCommand in domainCommands)
        {
            await _mediator.Send(domainCommand.GetType(), domainCommand);
        }
    }
    #endregion
}