using DotNetCore.CAP;
using Hotline.Application.JudicialManagement;
using Hotline.Application.Quality;
using Hotline.Application.Snapshot;
using Hotline.Caching.Interfaces;
using Hotline.Configurations;
using Hotline.EventBus;
using Hotline.FlowEngine.Notifications;
using Hotline.FlowEngine.WorkflowModules;
using Hotline.FlowEngine.Workflows;
using Hotline.JudicialManagement.Notifies;
using Hotline.KnowledgeBase;
using Hotline.Orders;
using Hotline.Push.Notifies;
using Hotline.Settings;
using Hotline.Settings.TimeLimitDomain;
using Hotline.Settings.TimeLimits;
using Hotline.Share.Dtos.FlowEngine.Workflow;
using Hotline.Share.Dtos.Order;
using Hotline.Share.Dtos.Settings;
using Hotline.Share.Enums.FlowEngine;
using Hotline.Share.Enums.Order;
using Hotline.Share.Enums.Push;
using Hotline.Share.Enums.Quality;
using Hotline.Share.Mq;
using Hotline.Snapshot.IRepository;
using Hotline.Snapshot.Notifications;
using Hotline.Users;
using Mapster;
using MapsterMapper;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using XF.Domain.Authentications;
using XF.Domain.Entities;
using XF.Domain.Exceptions;
using XF.Domain.Extensions;
using XF.Domain.Repository;

namespace Hotline.Application.Handlers.FlowEngine;

public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
{
    private readonly IOrderDomainService _orderDomainService;
    private readonly ICalcExpireTime _expireTime;
    private readonly IOrderSnapshotRepository _orderSnapshotRepository;
    private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
    private readonly IOrderRepository _orderRepository;
    private readonly ICapPublisher _capPublisher;
    private readonly IMapper _mapper;
    private readonly ILogger<WorkflowNextHandler> _logger;
    private readonly IQualityApplication _qualityApplication;
    private readonly IOrderDelayRepository _orderDelayRepository;
    private readonly IKnowledgeRepository _knowledgeRepository;
    private readonly ISessionContext _sessionContext;
    private readonly IRepository<User> _userRepository;
    private readonly IMediator _mediator;
    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
    private readonly Publisher _publisher;

    public WorkflowNextHandler(
        IOrderDomainService orderDomainService,
        IOrderRepository orderRepository,
        ICapPublisher capPublisher,
        IMapper mapper,
        ILogger<WorkflowNextHandler> logger,
        IQualityApplication qualityApplication,
        IKnowledgeRepository knowledgeRepository,
        ISessionContext sessionContext,
        IOrderDelayRepository orderDelayRepository,
        IRepository<User> userRepository,
        IMediator mediator,
        ISystemSettingCacheManager systemSettingCacheManager,
        Publisher publisher,
        IOptionsSnapshot<AppConfiguration> appOptions,
        ICalcExpireTime expireTime,
        IOrderSnapshotRepository orderSnapshotRepository)
    {
        _orderDomainService = orderDomainService;
        _orderRepository = orderRepository;
        _capPublisher = capPublisher;
        _mapper = mapper;
        _logger = logger;
        _qualityApplication = qualityApplication;
        _orderDelayRepository = orderDelayRepository;
        _knowledgeRepository = knowledgeRepository;
        _sessionContext = sessionContext;
        _userRepository = userRepository;
        _mediator = mediator;
        _systemSettingCacheManager = systemSettingCacheManager;
        _publisher = publisher;
        _appOptions = appOptions;
        _expireTime = expireTime;
        _orderSnapshotRepository = orderSnapshotRepository;
    }

    /// <summary>Handles a notification</summary>
    /// <param name="notification">The notification</param>
    /// <param name="cancellationToken">Cancellation token</param>
    public async Task Handle(NextStepNotify notification, CancellationToken cancellationToken)
    {
        try
        {
            //_logger.LogInformation(
            //    $"收到{nameof(NextStepNotify)}, notification: {JsonConvert.SerializeObject(notification)}");
            var workflow = notification.Workflow;
            var dto = notification.Dto;

            var currentTag = notification.Trace.Tag.TryDeserialize<DefinitionTag>();
            var nextTag = notification.NextStepDefine.Tag.TryDeserialize<DefinitionTag>();

            switch (workflow.ModuleCode)
            {
                case WorkflowModuleConsts.OrderHandle:
                case WorkflowModuleConsts.OrderHandleSnapshot:
                    var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId, withHotspot: true,
                        withAcceptor: true, withExtension: true, cancellationToken: cancellationToken);
                    order.CheckIfFiled();
                    order.UpdateHandlingStatus(workflow.IsInCountersign);
                    _mapper.Map(workflow, order);

                    var isCenterToOrg = dto.FlowDirection is EFlowDirection.CenterToOrg;
                    //if (isCenterToOrg)
                    //{
                    //    var expiredTimeConfig =
                    //        await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
                    //    order.CenterToOrg(expiredTimeConfig.TimeText, expiredTimeConfig.Count,
                    //        expiredTimeConfig.TimeType, expiredTimeConfig.ExpiredTime, expiredTimeConfig.NearlyExpiredTime
                    //        , expiredTimeConfig.NearlyExpiredTimeOne, notification.Dto.Opinion, notification.Trace.HandlerId,
                    //        notification.Trace.HandlerName, true);
                    //}

                    if (order.CounterSignType is ECounterSignType.Center &&
                        //notification.Trace.BusinessType is EBusinessType.Seat or EBusinessType.Send or EBusinessType.CenterLeader or EBusinessType.CenterMonitor &&
                        notification.Workflow.IsTopCountersignEndStep(notification.Trace) &&
                        isCenterToOrg)
                    {
                        bool.TryParse(
                            _systemSettingCacheManager.GetSetting(SettingConstants.IsResetCenterCountersignType)?.SettingValue[0],
                            out bool isResetCenterCountersignType);
                        if (isResetCenterCountersignType)
                            order.CounterSignType = null;
                    }

                    if (isCenterToOrg)
                        order.SendBackAuditEndTime = await _expireTime.GetWorkDay(DateTime.Now);
                    await _orderRepository.Updateable(order).ExecuteCommandAsync(cancellationToken);

                    //司法行政监督管理-推诿工单
                    //如果没开启则不处理
                    var isOpenJudicialManagement = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenJudicialManagement)?.SettingValue[0];
                    if (isOpenJudicialManagement == "true" && notification.Trace.StepType != EStepType.Summary &&
                        notification.Trace.StepType != EStepType.End && !notification.Trace.IsCountersignEndStep)
                        await _publisher.PublishAsync(new AddPassTheBuckOrderNotify(order, _sessionContext.RequiredOrgId, _sessionContext.OrgName),
                            PublishStrategy.ParallelWhenAll, cancellationToken);

                    try
                    {
                        if (notification.Dto.IsSms)
                        {
                            switch (notification.Dto.FlowAssignType)
                            {
                                case EFlowAssignType.Org:
                                    var orgCodes = notification.Trace.NextHandlers.Select(x =>
                                        x.OrgId); //notification.FlowAssignInfo.HandlerObjects.Select(x => x.Key);
                                    var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
                                    var orgList = await _userRepository.Queryable()
                                        .Where(x => orgCodes.Contains(x.OrgId) && x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id)))
                                        .ToListAsync(cancellationToken);
                                    foreach (var item in orgList)
                                    {
                                        if (!string.IsNullOrEmpty(item.PhoneNo))
                                        {
                                            var messageDto = new Share.Dtos.Push.MessageDto
                                            {
                                                PushBusiness = EPushBusiness.OrderSend,
                                                ExternalId = order.Id,
                                                OrderId = order.Id,
                                                PushPlatform = EPushPlatform.Sms,
                                                Remark = order.Title,
                                                Name = item.Name,
                                                TemplateCode = "1007",
                                                Params = new List<string>() { order.No },
                                                TelNumber = item.PhoneNo,
                                            };
                                            await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken);
                                        }
                                    }

                                    break;
                                case EFlowAssignType.User:
                                    var userCodes =
                                        notification.Trace.NextHandlers.Select(x =>
                                            x.UserId); //notification.FlowAssignInfo.HandlerObjects.Select(x => x.Key);
                                    var userList = await _userRepository.Queryable()
                                        .Where(x => userCodes.Contains(x.Id) && !string.IsNullOrEmpty(x.PhoneNo))
                                        .ToListAsync(cancellationToken);
                                    foreach (var item in userList)
                                    {
                                        var messageDto = new Share.Dtos.Push.MessageDto
                                        {
                                            PushBusiness = EPushBusiness.OrderSend,
                                            ExternalId = order.Id,
                                            OrderId = order.Id,
                                            PushPlatform = EPushPlatform.Sms,
                                            Remark = order.Title,
                                            Name = item.Name,
                                            TemplateCode = "1007",
                                            Params = new List<string>() { order.No },
                                            TelNumber = item.PhoneNo,
                                        };
                                        await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken);
                                    }

                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    catch
                    {
                    }

                    //各级汇总节点无需推送至省平台
                    if (notification.Trace.StepType is not EStepType.Summary &&
                        !notification.Trace.IsCountersignEndStep)
                    {
                        var orderDto = _mapper.Map<OrderDto>(order);
                        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowHandled, new OrderFlowDto
                        {
                            Order = orderDto,
                            WorkflowTrace = _mapper.Map<WorkflowTraceDto>(notification.Trace),
                            ExpiredTimeChanged = isCenterToOrg,
                            HandlerOrgLevel = notification.HandlerOrgId.CalcOrgLevel()
                        }, cancellationToken: cancellationToken);
                    }

                    if (isCenterToOrg)
                        await _qualityApplication.AddQualityAsync(EQualitySource.Send, order.Id, cancellationToken);

                    //随手拍推送网格员
                    if (_systemSettingCacheManager.Snapshot)
                    {
                        if (string.Compare(TagDefaults.Wanggeyuan, notification.NextStepDefine.Tag,
                                StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            await _publisher.PublishAsync(new PostGuiderSystemNotification(order.Id),
                                PublishStrategy.ParallelWhenAll, cancellationToken);
                        }
                    }

                    break;
                case WorkflowModuleConsts.KnowledgeAdd:
                case WorkflowModuleConsts.KnowledgeUpdate:
                case WorkflowModuleConsts.KnowledgeOffshelf:
                case WorkflowModuleConsts.KnowledgeDelete:
                    //var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                    var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                    knowledge.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                    await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
                    break;

                case WorkflowModuleConsts.OrderDelay:
                    var orderDelay = await _orderDelayRepository.Queryable().Includes(x => x.Order)
                        .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                    if (orderDelay != null)
                    {
                        orderDelay.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                        await _orderDelayRepository.UpdateAsync(orderDelay, cancellationToken);
                    }


                    if (nextTag is not null && nextTag.Type == TagDefaults.TagType.Org)
                    {
                        switch (nextTag.Value)
                        {
                            case TagDefaults.TagValue.Province:
                                //TODO 发起省延期审批
                                //var orderDelay = await _orderDelayRepository.Queryable().Includes(x => x.Order)
                                //    .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                                try
                                {
                                    if (orderDelay != null)
                                    {
                                        orderDelay.DelayApplyType = EDelayApplyType.ProvinceApply;
                                        orderDelay.IsProDelay = true;
                                        await _orderDelayRepository.UpdateAsync(orderDelay);
                                        //省件延期--以省审批前一个节点整理的延期意见为准推送省上 宜宾
                                        if (_appOptions.Value.IsYiBin)
                                        {
                                            orderDelay.DelayReason = notification.Dto.Opinion;
                                        }

                                        //推送
                                        var publishOrderDelay = _mapper.Map<PublishOrderDelayDto>(orderDelay);
                                        await _capPublisher.PublishAsync(EventNames.HotlineOrderApplyDelay, publishOrderDelay,
                                            cancellationToken: cancellationToken);

                                        //await _provinceService.DelayCaseInfoSend(publishOrderDelay, cancellationToken);
                                    }
                                }
                                catch (Exception e)
                                {
                                    _logger.LogError("_provinceService.DelayCaseInfoSend throw exception: {ex}",
                                        e.Message);
                                }

                                break;
                        }
                    }

                    break;
            }
        }
        catch (Exception e)
        {
            _logger.LogError($"{nameof(WorkflowNextHandler)}异常，{e}");
            throw;
        }
    }
}