using IRaCIS.Core.Domain;
using IRaCIS.Core.Domain.Models;
using MassTransit;
using MassTransit.Mediator;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System.Data;

namespace IRaCIS.Core.Infra.EFCore.Interceptor
{
    //   命令（Command）：是一种定向的消息，应该由一个特定的接收者处理。用 Send 发送。
    //   事件（Event）：是一种广播式消息，可以有多个订阅者接收并处理。用 Publish 发布。
    //   IPublishEndpoint：提供了Publish方法，用于发布事件,该接口仅负责发布消息，不会依赖于接收者的存在，发布者和消费者可以是完全分离的
    //   IMediator  适用于单进程内的消息传递，不涉及外部消息中间件（如 RabbitMQ、Azure Service Bus 等）  使用本地的内存队列，而不是跨进程消息传递，适合轻量级、无中间件的场景
    //   发件箱模式参考：  https://dev.to/antonmartyniuk/use-masstransit-to-implement-outbox-pattern-with-ef-core-and-mongodb-oep#:~:text=MongoDB%20replica%20set%20is%20required%20for%20both%20publisher%20and%20consumer
    // 1、IPublishEndpoint 才会将事件存储到发件箱表中， 高级IBus接口时 - 消息不会存储在发件箱中，必须有savechanges 才会一起提交保存到数据库中
    // 2、进入消息代理之前，发布事件在OutboxState  OutboxMessage，   进入消费者以后（已经删除OutboxState  OutboxMessage），消费失败，需要修改代码重新发布，然后之前消费事件的重新处理，错误处理参考：https://www.youtube.com/watch?v=3TMKUu7c4lc
    public class DispatchDomainEventsInterceptor(/*IMediator _mediator,*/ IMessageScheduler _scheduler, IPublishEndpoint _publishEndpoint) : SaveChangesInterceptor
    {

        //领域事件通常与数据变更密切相关。如果在 SaveChanges 之前发布事件，有可能事件发布时的数据状态还没有被持久化到数据库。这可能导致事件消费者看到的是一个不一致的状态

        /// <summary>
        /// 在事务提交之后分发事件
        /// </summary>
        /// <param name="eventData"></param>
        /// <param name="result"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override async ValueTask<int> SavedChangesAsync(SaveChangesCompletedEventData eventData, int result,
            CancellationToken cancellationToken = default)
        {
            await DispatchDomainEvents(eventData.Context);
            return await base.SavedChangesAsync(eventData, result, cancellationToken);
        }
        public override int SavedChanges(SaveChangesCompletedEventData eventData, int result)
        {
            DispatchDomainEvents(eventData.Context).GetAwaiter().GetResult();
            return base.SavedChanges(eventData, result);
        }
        private async Task DispatchDomainEvents(DbContext? context)
        {
            if (context == null) return;

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

            var domainEvents = entities
                .SelectMany(e => e.DomainEvents)
                .ToList();

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

            foreach (var domainEvent in domainEvents)
            {
                //这种方式会导致消息没处理
                //await publishEndpoint.Publish(domainEvent);

                if (domainEvent.IsDelayScheduleEvent)
                {
                    Console.WriteLine("qc领取任务消息发布：" + DateTime.Now.ToShortTimeString() + $"延时{domainEvent.DelaySeconds}");
                    //延迟调度的消息，比如1h后再消费
                    await _scheduler.SchedulePublish(DateTime.Now.AddSeconds((int)domainEvent.DelaySeconds!), (object)domainEvent);
                }
                else
                {
                    await _publishEndpoint.Publish(domainEvent.GetType(), domainEvent);

                }

            }
        }

    }
}
