using DotNetCore.CAP;
using Hotline.Caching.Interfaces;
using Hotline.Share.Dtos.Order;
using Hotline.Share.Enums.Order;
using MapsterMapper;
using Microsoft.Extensions.Logging;
using XF.Domain.Authentications;
using XF.Domain.Cache;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Repository;
using Hotline.CallCenter.Calls;
using Hotline.File;
using Hotline.FlowEngine.Workflows;
using Hotline.Schedulings;
using Hotline.SeedData;
using Hotline.Users;
using Hotline.Share.Dtos;
using Hotline.Settings.Hotspots;
using Hotline.Share.Dtos.FlowEngine;
using Microsoft.AspNetCore.Http;
using Hotline.Settings;
using SqlSugar;
using Hotline.Push.Notifies;
using Hotline.Share.Enums.Push;
using MediatR;
using Hotline.Authentications;
using Hotline.ContingencyManagement.Notifies;
using Hotline.EventBus;
using Hotline.Share.Enums.FlowEngine;
using Mapster;
using Microsoft.AspNetCore.Builder.Extensions;
using Hotline.Configurations;
using Microsoft.Extensions.Options;
using Hotline.Share.Tools;
using Hotline.Orders.Notifications;
using Hotline.Share.Mq;
using System.Security.Cryptography;
using Hotline.FlowEngine.Definitions;
using Hotline.Share.Dtos.FlowEngine.Workflow;
using Hotline.Share.Enums.Settings;
using Hotline.Settings.TimeLimitDomain;
using Hotline.Snapshot.IRepository;
using Hotline.Share.Dtos.Push;
using System.Threading;
using XF.Domain.Entities;

namespace Hotline.Orders;

public class OrderDomainService : IOrderDomainService, IScopeDependency
{
    private readonly IOrderRepository _orderRepository;
    private readonly IRepository<OrderRedo> _orderRedoRepository;
    private readonly IRepository<OrderPublish> _orderPublishRepository;
    private readonly IRepository<OrderVisit> _orderVisitRepository;
    private readonly IRepository<OrderExtension> _orderExtensionRepository;
    private readonly IRepository<OrderComplement> _orderComplementRepository;
    private readonly ITypedCache<CacheOrderNO> _cacheOrderNo;
    private readonly ISessionContext _sessionContext;
    private readonly ICapPublisher _capPublisher;
    private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
    private readonly IMapper _mapper;
    private readonly ILogger<OrderDomainService> _logger;
    private readonly IFileRepository _fileRepository;
    private readonly IRepository<Scheduling> _schedulingRepository;
    private readonly Publisher _publisher;
    private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
    private readonly IRepository<User> _userRepository;
    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
    private readonly IWorkflowDomainService _workflowDomainService;
    private readonly IRepository<Hotspot> _hotspotRepository;
    private readonly IMediator _mediator;
    private readonly IRepository<WorkflowStep> _workflowStepRepository;
    private readonly ISystemLogRepository _systemLogRepository;
    private readonly IOrderSnapshotRepository _orderSnapshotRepository;
    private readonly ICalcExpireTime _expireTime;
    private readonly ICallDomainService _callDomainService;
    private readonly IOrderVisitDomainService _orderVisitDomainService;
	

	public OrderDomainService(
        IOrderRepository orderRepository,
        IRepository<OrderRedo> orderRedoRepository,
        IRepository<OrderPublish> orderPublishRepository,
        IRepository<OrderVisit> orderVisitRepository,
        IRepository<OrderExtension> orderExtensionRepository,
        IRepository<OrderComplement> orderComplementRepository,
        ITypedCache<CacheOrderNO> cacheOrderNo,
        ISessionContext sessionContext,
        ICapPublisher capPublisher,
        IMapper mapper,
        ILogger<OrderDomainService> logger,
        IFileRepository fileRepository,
        IRepository<WexCallRecord> wexCallRecordRepository,
        IRepository<User> userRepository,
        ISystemSettingCacheManager systemSettingCacheManager,
        IRepository<Scheduling> schedulingRepository,
        IWorkflowDomainService workflowDomainService,
        IRepository<Hotspot> hotspotRepository,
        IMediator mediator,
        Publisher publisher,
        IRepository<OrderVisitDetail> orderVisitDetailRepository,
        IRepository<WorkflowStep> workflowStepRepository,
        ISystemLogRepository systemLogRepository,
        IOptionsSnapshot<AppConfiguration> appOptions,
        IOrderSnapshotRepository orderSnapshotRepository,
        ICalcExpireTime expireTime,
        ICallDomainService callDomainService,
        IOrderVisitDomainService orderVisitDomainService)
    {
        _orderRepository = orderRepository;
        _orderRedoRepository = orderRedoRepository;
        _orderPublishRepository = orderPublishRepository;
        _orderVisitRepository = orderVisitRepository;
        _orderExtensionRepository = orderExtensionRepository;
        _orderComplementRepository = orderComplementRepository;
        _cacheOrderNo = cacheOrderNo;
        _sessionContext = sessionContext;
        _capPublisher = capPublisher;
        _mapper = mapper;
        _logger = logger;
        _fileRepository = fileRepository;
        _userRepository = userRepository;
        _schedulingRepository = schedulingRepository;
        _systemSettingCacheManager = systemSettingCacheManager;
        _workflowDomainService = workflowDomainService;
        _hotspotRepository = hotspotRepository;
        _mediator = mediator;
        _publisher = publisher;
        _orderVisitDetailRepository = orderVisitDetailRepository;
        _workflowStepRepository = workflowStepRepository;
        _systemLogRepository = systemLogRepository;
        _appOptions = appOptions;
        _orderSnapshotRepository = orderSnapshotRepository;
        _expireTime = expireTime;
        _callDomainService = callDomainService;
        _orderVisitDomainService = orderVisitDomainService;
	}

    /// <summary>
    /// 归档后自动发布
    /// </summary>
    /// <param name="order"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task OrderAutomaticPublishAsync(Order order, CancellationToken cancellationToken)
    {
        var name = "中心直办件归档后默认自动发布";
        var msg = $"OrderId: {order.Id}, No: {order.No}";
        try
        {
            if (_systemSettingCacheManager.AutomaticPublishOrder == false)
            {
                return;
            }
            var isSnapshot = await _orderSnapshotRepository.AnyAsync(m => m.Id == order.Id, cancellationToken);
            if (isSnapshot == true)
            {
                _systemLogRepository.Add(name, msg, "随手拍工单不允许自动发布");
                return;
            }
            var checkResult = order.AutoPublishCheck();
            if (checkResult.NotNullOrEmpty())
            {
                _systemLogRepository.Add(name, msg, checkResult);
                return;
            }

            if (order.FileOrgIsCenter == null || order.FileOrgIsCenter == false)
            {
                _systemLogRepository.Add(name, msg, "非中心直办件");
                return;
            }

            await OrderPublishAsync(order, cancellationToken);
        }
        catch (Exception e)
        {
            _systemLogRepository.Add(name, msg, $"系统异常: {e.Message}");
        }
    }

    public async Task OrderPublishAsync(Order order, CancellationToken cancellationToken)
    {
        OrderPublish orderPublish = new()
        {
            OrderId = order.Id,
            No = order.No,
            PublishState = false, //当前写死为false
            ArrangeTitle = order.Title,
            ArrangeContent = order.Content,
            ArrangeOpinion = order.FileOpinion,
            ProPublishState = false,
            FeedBackPhone = order.Contact,
            CreatorName = _sessionContext.UserName
        };
        var id = await _orderPublishRepository.AddAsync(orderPublish, cancellationToken);
        order.Publish(orderPublish.PublishState);
        await _orderRepository.UpdateAsync(order, cancellationToken);

        //推省上
        var publishPublishOrder = orderPublish.Adapt<PublishPublishOrderDto>();
        publishPublishOrder.Order = order.Adapt<OrderDto>();
        //查询实际办理附件
        if (!string.IsNullOrEmpty(order.ActualHandleStepId))
        {
            var actualHandleStep = await _workflowStepRepository.GetAsync(order.ActualHandleStepId, cancellationToken);
            publishPublishOrder.FileJsons = actualHandleStep?.FileJson;
        }

        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderPublishOrder, publishPublishOrder, cancellationToken: cancellationToken);

        //推应急管理局
        //是否开启
        var isOpenContingencyManagement =
            _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenContingencyManagement)?.SettingValue[0];
        if (isOpenContingencyManagement == "true")
            await _publisher.PublishAsync(new ContingencyManagementNotify(order, order.Title, order.Content, order.ActualOpinion),
                PublishStrategy.ParallelWhenAll, cancellationToken);

        // 取消发布的工单数量
        var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true)
            .Where(m => m.OrderId == order.Id && m.IsDeleted == true)
            .CountAsync(cancellationToken);
        var orderVisitVisitedCount = await _orderVisitRepository.Queryable()
            .Where(m => m.OrderId == order.Id && m.VisitState == EVisitState.Visited)
            .CountAsync(cancellationToken);

        // 若取消发布的工单，已经被回访过了，没有经过重新办理，再次发布后，自动跳过回访环节，展示取消发布前的回访结果
        if (orderPublishDeletedCount != 0 && orderVisitVisitedCount != 0)
        {
            return;
        }
        var orderVisit = new OrderVisit
        {
            No = order.No,
            OrderId = order.Id,
            VisitState = EVisitState.WaitForVisit,
            PublishTime = DateTime.Now,
            IsCanHandle = true,
            EmployeeId = _sessionContext.RequiredUserId
        };
        if (!order.IsProvince)
        {
            orderVisit.EmployeeId = _sessionContext.RequiredUserId;
        }

        var isReturn = false;
        if (_appOptions.Value.IsZiGong)
        {
            orderVisit.EmployeeId = string.Empty;

            //自贡任务  348 语音评价不满意的工单需回访
            var callNative = await GetReplyVoiceOrDefaultByOrderIdAsync(orderVisit.OrderId);
            if (callNative == EVoiceEvaluate.NoSatisfied || callNative == EVoiceEvaluate.VeryNoSatisfied)
                isReturn = true;
        }

        if (isReturn == false)
        {
            //order.FocusOnEvents!="7" 2025年4月29日 10:55:38 刘欣 临时新增，如果是旅游件不自动回访
            if (order is { FileOrgIsCenter: true, CounterSignType: null } && !order.IsProvince && order.FocusOnEvents!="7")
            {
                orderVisit.VisitState = EVisitState.Visited;
                orderVisit.VisitTime = DateTime.Now;
                orderVisit.VisitType = EVisitType.OtherVisit;
                orderVisit.NowEvaluate = new Kv() { Key = "4", Value = "满意" };
                if (_appOptions.Value.IsZiGong)
                {
                    // 根据禅道 自贡需求 Id_361, 第一条, 3小条需求;
                    // 直办件归档后自动回访量需统计在“胡玲”的默认回访量中;
                    orderVisit.EmployeeId = _systemSettingCacheManager.DefaultVisitEmployeeId;
                }
            }

            if (order.CounterSignType != ECounterSignType.Center && order.FocusOnEvents != "7")
            {
                orderVisit.IsCanAiVisit = true;
            }
        }
        string visitId = await _orderVisitRepository.AddAsync(orderVisit);

        //新增回访信息
        var visitedDetail = new List<OrderVisitDetail>();

        //坐席回访明细
        var seatDetail = new OrderVisitDetail
        {
            VisitId = visitId,
            VisitTarget = EVisitTarget.Seat
        };

        //部门回访明细
        var orgDetail = new OrderVisitDetail
        {
            VisitId = visitId,
            VisitOrgCode = order.ActualHandleOrgCode,
            VisitOrgName = order.ActualHandleOrgName,
            VisitTarget = EVisitTarget.Org
        };

        if (isReturn == false)
        {
            //order.FocusOnEvents!="7" 2025年4月29日 10:55:38 刘欣 临时新增，如果是旅游件不自动回访
            if (order is { FileOrgIsCenter: true, CounterSignType: null, IsProvince: false } && order.FocusOnEvents != "7")
            {
                var satisfy = new Kv() { Key = "4", Value = "满意" };
                orgDetail.OrgProcessingResults = satisfy;
                //orgDetail.OrgHandledAttitude = satisfy;
            }

            if (order is { FileOrgIsCenter: true, CounterSignType: null })
            {
                seatDetail.VoiceEvaluate = EVoiceEvaluate.Satisfied;
                seatDetail.SeatEvaluate = ESeatEvaluate.Satisfied;
                order.Visited("4", "满意");
                order.Status = EOrderStatus.Visited;
                await _orderRepository.UpdateAsync(order, cancellationToken);
            }
        }

        visitedDetail.Add(orgDetail);
        visitedDetail.Add(seatDetail);

        await _orderVisitDetailRepository.AddRangeAsync(visitedDetail, cancellationToken);
        await _publisher.PublishAsync(new ContingencyManagementNotify(order, order.Title, order.Content, order.ActualOpinion),
    PublishStrategy.ParallelWhenAll, cancellationToken);

        //handle publish trace
        var acceptor = new UserInfo(orderPublish.CreatorId, orderPublish.CreatorName,
            orderPublish.CreatorOrgId, orderPublish.CreatorOrgName);
        var handler = new UserInfo(orderPublish.CreatorId, orderPublish.CreatorName,
            orderPublish.CreatorOrgId, orderPublish.CreatorOrgName);
        //上面回访人取值当前操作人，所以暂时可取值SessionContext,如需求有变需取值ordervisit中指定的回访人
        var visitAcceptor = string.IsNullOrEmpty(orderVisit.EmployeeId)
            ? new UserInfo()
            : new UserInfo(_sessionContext.UserId, _sessionContext.UserName, _sessionContext.OrgId,
                _sessionContext.OrgName, _sessionContext.OrgIsCenter);
        await _workflowDomainService.HandlePublishTraceAsync(order.WorkflowId, orderPublish.Id, acceptor, handler,
            orderPublish.CreationTime, visitAcceptor, orderVisit.Id, cancellationToken);

        //需求251  某些工单需自动发送短信
        //任何类型的省工单都不需要发送短信
        var isOpenSendEndSms = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenSendEndSms)?.SettingValue[0];
        if (isOpenSendEndSms == "true")
        {
            try
            {
                if (_appOptions.Value.IsZiGong == false)
                {
                    if (order.Source != ESource.ProvinceStraight && order.FileOrgIsCenter.Value == false)
                    {
                        var isOpenSendVisitSms = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenSendVisitSms)?.SettingValue[0];
                        var code = "";
                        //受理类型为“投诉、举报”
                        if ((order.AcceptTypeCode == "30" || order.AcceptTypeCode == "35") && orderVisit.VisitState != EVisitState.Visited && isOpenSendVisitSms == "true")
                        {
                            code = "1017";
                            orderVisit.VisitState = EVisitState.SMSVisiting;
                            await _orderVisitRepository.UpdateAsync(orderVisit);
                        }
                        else
                            code = "1018";

                        //发送查询短信
                        var messageDto = new Share.Dtos.Push.MessageDto
                        {
                            PushBusiness = EPushBusiness.SearchSms,
                            ExternalId = visitId,
                            OrderId = order.Id,
                            PushPlatform = EPushPlatform.Sms,
                            Remark = order.Title,
                            Name = order.FromName,
                            TemplateCode = code,
                            Params = new List<string>() { order.No, order.Password },
                            TelNumber = order.Contact,
                        };
                        await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken);

                        // 发送短信后推送一个 48小时的延迟消息队列. 当消息队列收到消息时, 判断用户是否回复了, 如果未回复短信就 默认满意
                        var delaySecond = _systemSettingCacheManager.DefaultVisitSmsDelaySecond;
                        await _capPublisher.PublishDelayAsync(
                            TimeSpan.FromSeconds(delaySecond),
                            EventNames.UpdateVisitDelaySms,
                            messageDto,
                            cancellationToken: cancellationToken);
                    }
                }
            }
            catch (Exception)
            {
            }
        }
        if (order.IsProvince == false && orderVisit.VisitState == EVisitState.Visited)
        {
            //推省上
            await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisited,
                new PublishVisitDto()
                {
                    Order = order.Adapt<OrderDto>(),
                    No = orderVisit.No,
                    VisitType = orderVisit.VisitType,
                    VisitName = orderVisit.CreatorName,
                    VisitTime = orderVisit.VisitTime,
                    VisitRemark = orderVisit.NowEvaluate?.Value,
                    AreaCode = order.AreaCode!,
                    SubjectResultSatifyCode = orderVisit.NowEvaluate?.Key,
                    FirstSatisfactionCode = orderVisit.NowEvaluate?.Key,
                    ClientGuid = ""
                }, cancellationToken: cancellationToken);
        }

        //推门户
        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto()
        {
            Id = orderVisit.Id,
            Order = order.Adapt<OrderDto>(),
            OrderVisitDetails = orderVisit.OrderVisitDetails.Adapt<List<VisitDetailDto>>(),
            VisitName = _sessionContext.UserName,
            VisitTime = orderVisit.VisitTime,
            VisitType = orderVisit.VisitType,
            VisitState = orderVisit.VisitState,
            PublishTime = orderVisit.PublishTime,
        }, cancellationToken: cancellationToken);
    }

    /// <summary>
    /// 重办和退回工单时如果有取消发布的工单, 清除回访待办和回访列表中的数据
    /// </summary>
    /// <param name="orderId"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task VisitNoneByCancelPublishAsync(string orderId, CancellationToken cancellationToken)
    {
        var cancelPublishOrderEnabled = _systemSettingCacheManager.CancelPublishOrderEnabled;

        // 取消发布的工单数量
        var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true)
            .Where(m => m.OrderId == orderId && m.IsDeleted == true)
            .CountAsync(cancellationToken);

        if (orderPublishDeletedCount == 0 || cancelPublishOrderEnabled == false) return;

        var visit = await _orderVisitRepository.GetAsync(x => x.OrderId == orderId && x.VisitState != EVisitState.None, cancellationToken);
        if (visit != null)
        {
            visit.VisitState = EVisitState.None;
            await _orderVisitRepository.UpdateAsync(visit, cancellationToken);
        }
    }

    public async Task<Order> GetOrderAsync(string? orderId, bool withHotspot = false, bool withAcceptor = false,
        bool withExtension = false, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrEmpty(orderId))
            throw UserFriendlyException.SameMessage("无效工单编号");

        var query = _orderRepository.Queryable().Includes(d => d.OrderPushTypes);
        if (withHotspot)
            query = query.Includes(d => d.Hotspot);
        if (withAcceptor)
            query = query.Includes(d => d.Acceptor);
        if (withExtension)
            query = query.Includes(d => d.OrderExtension);

        var order = await query.FirstAsync(d => d.Id == orderId, cancellationToken);
        if (order == null)
            throw new UserFriendlyException($"无效工单编号, orderId: {orderId}", "无效工单编号");
        return order;
    }

    public async Task<string> AddAsync(Order order, bool autoAccept = false, CancellationToken cancellationToken = default)
    {
        if (autoAccept)
        {
            order.AutoAccept(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.StaffNo);
            order.Sign(_sessionContext.RequiredUserId, _sessionContext.UserName);
        }
        order.Init();
        order.No = GenerateNewOrderNo();
        if (order.Source == ESource.Police110)
        {
            order.Title = @"(非警情工单-" + order.No.Substring(order.No.Length - 6) + ")";
        }
        order.Password = Random.Shared.Next(100000, 1000000).ToString();
        order.ProvinceNo = string.IsNullOrEmpty(order.ProvinceNo) ? GenerateNewProvinceNo(order.No, order.SourceChannelCode) : order.ProvinceNo;
        return await _orderRepository.AddOrderNavAsync(order, cancellationToken);
    }

    /// <summary>
    /// 撤回或跳转前处理数据及校验
    /// <remarks>
    ///工单撤回时需校验工单当前是否存在待发布记录、待回访记录，若存在需删除对应记录（跳转同理）
    ///工单撤回时需校验工单是否存在甄别中记录，若存在不允许撤回当前工单（跳转同理）
    /// </remarks>
    /// </summary>
    public async Task ReadyToRecallAsync(string orderId, CancellationToken cancellationToken)
    {
        var order = await _orderRepository.Queryable()
            .Includes(d => d.OrderPublish)
            .Includes(d => d.OrderVisits.Where(d => d.VisitState == EVisitState.WaitForVisit))
            .Includes(d => d.OrderScreens)
            .FirstAsync(d => d.Id == orderId, cancellationToken);

        if (order.OrderScreens.Any())
            throw UserFriendlyException.SameMessage("已甄别工单无法撤回或跳转");

        //todo 需求调整：P72， 4，5，6，7，8，9，10

        if (order.OrderPublish is not null)
            await _orderPublishRepository.RemoveAsync(order.OrderPublish, cancellationToken: cancellationToken);

        if (order.OrderVisits.Any())
            await _orderVisitRepository.RemoveNav(order.OrderVisits)
                .Include(d => d.OrderVisitDetails)
                .ExecuteCommandAsync();
    }

    public Task<string> AddOrderRedoAsync(OrderRedo orderRedo, CancellationToken cancellationToken) =>
        _orderRedoRepository.AddAsync(orderRedo, cancellationToken);

    public Task RemoveOrderRedoAsync(string id, CancellationToken cancellationToken) =>
        _orderRedoRepository.RemoveAsync(id, cancellationToken: cancellationToken);

    public Task UpdateOrderRedoAsync(OrderRedo orderRedo, CancellationToken cancellationToken) =>
        _orderRedoRepository.UpdateAsync(orderRedo, cancellationToken);

    #region 工单扩展信息

    public async Task<OrderExtension?> GetOrderExtensionsAsync(string provinceNo, CancellationToken cancellationToken) =>
        await _orderExtensionRepository.GetAsync(d => d.ProvinceNo == provinceNo, cancellationToken);

    public Task UpdateExtensionAsync(OrderExtension orderExtension, CancellationToken cancellationToken) =>
        _orderExtensionRepository.UpdateAsync(orderExtension, cancellationToken);


    /// <summary>
    /// 新增工单扩展信息
    /// </summary>
    public async Task AddExtensionAsync(OrderExtension extension, CancellationToken cancellationToken) =>
        await _orderExtensionRepository.AddAsync(extension, cancellationToken);

    /// <summary>
    /// 新增工单补充信息
    /// </summary>
    public async Task<string> AddOrderComplementAsync(AddOrderComplementDto dto, CancellationToken cancellationToken)
    {
        var complement = _mapper.Map<OrderComplement>(dto);
        complement.InitId();
        if (dto.Files.Any()) complement.FileJson = await _fileRepository.AddFileAsync(dto.Files, complement.Id, "", cancellationToken);
        return await _orderComplementRepository.AddAsync(complement, cancellationToken);
    }


    #endregion

    /// <summary>
    /// 验证是否是管理员
    /// </summary>
    /// <returns></returns>
    public bool IsCheckAdmin()
    {
        var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];
        if (!string.IsNullOrEmpty(systemAdministrator) && (_sessionContext.Roles.Contains(systemAdministrator) || _sessionContext.Roles.Contains(RoleSeedData.AdminRole)))
            return true;
        else
            return false;
    }



    #region 平均派单

    public async Task<bool> SchedulingAtWork(string UserId)
    {
        var time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
        var atWork = await _schedulingRepository.Queryable()
            .Includes(x => x.SchedulingUser)
            .Where(x => x.SchedulingUser.UserId == UserId && x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.AtWork == true)
            .AnyAsync();
        return atWork;
    }
    /// <summary>
    /// 平均派单
    /// </summary>
    /// <returns></returns>
    public async Task<StepAssignInfo> AverageOrder(CancellationToken cancellationToken)
    {
        var time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
        var scheduling = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
            .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.AtWork == true)
            .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
        var roleId = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];
        if (scheduling is null)
            return new StepAssignInfo
            {
                Key = AppDefaults.SendPoolId,
                Value = "待派单池",
                UserId = AppDefaults.SendPoolId,
                Username = "待派单池",
                OrgId = OrgSeedData.CenterId,
                OrgName = centerOrgName,
                RoleId = roleId,
                RoleName = "派单员",
                FlowAssignType = EFlowAssignType.User,
            };
        scheduling.SendOrderNum++;
        await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);
        var user = scheduling.SchedulingUser;
        return new StepAssignInfo
        {
            Key = user.UserId,
            Value = user.UserName,
            UserId = user.UserId,
            Username = user.UserName,
            OrgId = user.OrgId,
            OrgName = user.OrgIdName,
            RoleId = roleId,
            RoleName = "派单员",
            FlowAssignType = EFlowAssignType.User,
        };
    }

    /// <summary>
    /// 登录平均派单
    /// </summary>
    /// <param name="userId"></param>
    /// <returns></returns>
    public async Task LogAverageOrder(string userId, Scheduling scheduling, CancellationToken cancellationToken)
    {
        //1.获取默认派单员所属的工单
        //2.获取今天上班的人员
        //3.给当前这个用户平均派单

        var steps = await _workflowDomainService.GetStepsBelongsToAsync(AppDefaults.SendPoolId,
           cancellationToken);
        var roleId = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];

        var user = await _userRepository.Queryable()
            .Includes(d => d.Organization)
            .FirstAsync(d => d.Id == userId, cancellationToken);
        var time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
        var schedulings = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
            .Where(x => x.SchedulingTime == time).CountAsync(cancellationToken);
        if (schedulings > 0)
        {
            var sendNum = steps.Count / schedulings;
            scheduling.SendOrderNum += sendNum;
            if (!scheduling.LoginSendOrderNum.HasValue)
            {
                scheduling.LoginSendOrderNum = scheduling.LoginSendOrderNum.HasValue && scheduling.LoginSendOrderNum > sendNum ? scheduling.LoginSendOrderNum : sendNum;
                await _schedulingRepository.Updateable()
                    .SetColumns(s => new Scheduling() { LoginSendOrderNum = scheduling.LoginSendOrderNum })
                    .Where(s => s.SchedulingTime == scheduling.SchedulingTime).ExecuteCommandAsync(cancellationToken);
            }
            sendNum = scheduling.LoginSendOrderNum.Value;
            await _schedulingRepository.Updateable()
                .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum, AtWork = scheduling.AtWork })
                .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);

            if (sendNum <= 0) return;
            var sendSteps = steps.Take(sendNum).ToList();
            await _orderRepository.Updateable().SetColumns(o => new Order()
            {
                CenterToOrgHandlerId = user.OrgId,
                CenterToOrgHandlerName = user.Name
            }).Where(o => sendSteps.Any(s => s.ExternalId == o.Id)).ExecuteCommandAsync(cancellationToken);

            await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)>
            {
                new(user.Id, user.Name, user.OrgId, user.Organization.Name,roleId,"派单员", sendSteps)
            }, cancellationToken);
        }

    }

    /// <summary>
    /// 触发平均派单
    /// </summary>
    /// <returns></returns>
    public async Task TriggerAverageOrder(CancellationToken cancellationToken)
    {
		// 增加回收机制  先回收有未处理的工单 再进行平均派单
		//1.从排班里面获取今天上班的人
		//2.获取默认派单员剩下的工单
		//3.平均分配剩下的工单给今天上班的人
		#region 回收机制
		DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
		var roleId = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];

		var schedulingUsers = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
			.Where(x => x.SchedulingTime!.Value == time && (x.AtWork == null || x.AtWork == false) )
            .Select(x=> new SchedulingUser { UserId = x.SchedulingUser.UserId } )
			.ToListAsync(cancellationToken);
        var userIds = schedulingUsers.Select(x => x.UserId).ToList();
		var usersSteps = await _workflowDomainService.GetStepsBelongsDayToAsync(userIds, time, cancellationToken);
        if (usersSteps != null && usersSteps.Any())
        {
			await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)>
			{
				new(AppDefaults.SendPoolId, "派单池", OrgSeedData.CenterId, OrgSeedData.CenterName,roleId,"派单员", usersSteps)
			}, cancellationToken);
		}
		#endregion
		var schedulings = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
            .Where(x => x.SchedulingTime!.Value == time && x.WorkingTime!.Value <= DateTime.Now.TimeOfDay && x.OffDutyTime!.Value >= DateTime.Now.TimeOfDay && x.AtWork == true)
            .OrderBy(x => x.SendOrderNum).ToListAsync(cancellationToken);
        if (schedulings.Any())
        {
            var steps = await _workflowDomainService.GetStepsBelongsToAsync(AppDefaults.SendPoolId,
                cancellationToken);
            if (steps.Any())
            {
                List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)> handlers = new();
                var avg = steps.Count / schedulings.Count;
                var remaining = steps.Count % schedulings.Count;
                var skip = 0;
                for (var i = 0; i < schedulings.Count; i++)
                {
                    var scheduling = schedulings[i];
                    var size = avg + (i < remaining ? 1 : 0);
                    if (size > 0)
                    {
                        var stSteps = steps.Skip(skip).Take(size).ToList();

                        handlers.Add(new(
                            scheduling.SchedulingUser.UserId,
                            scheduling.SchedulingUser.UserName,
                            scheduling.SchedulingUser.OrgId,
                            scheduling.SchedulingUser.OrgIdName,
                            roleId, "派单员",
                            stSteps));
                        skip += size;
                        scheduling.SendOrderNum += size;
                        await _schedulingRepository.Updateable()
                            .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum })
                            .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
                        await _orderRepository.Updateable().SetColumns(o => new Order()
                        {
                            CenterToOrgHandlerId = scheduling.SchedulingUser.UserId,
                            CenterToOrgHandlerName = scheduling.SchedulingUser.UserName
                        })
                            .Where(o => stSteps.Any(s => s.ExternalId == o.Id)).ExecuteCommandAsync(cancellationToken);
                    }
                }
                if (handlers.Any())
                    await _workflowDomainService.ChangeHandlerBatchAsync(handlers, cancellationToken);
            }
        }
    }

	/// <summary>
	/// 提前触发平均派单
	/// </summary>
	/// <param name="cancellationToken"></param>
	/// <returns></returns>
	public async Task AdvancedTriggerAverageOrder(CancellationToken cancellationToken) 
    {
		//1.从排班里面获取今天上班的人
		//2.获取默认派单员剩下的工单
		//3.平均分配剩下的工单给今天上班的人
		DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
        DateTime fallsTime = DateTime.Parse("00:00:00");
		var schedulings = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
			.Where(x => x.SchedulingTime!.Value == time && x.WorkingTime!.Value != fallsTime.TimeOfDay && x.OffDutyTime!.Value != fallsTime.TimeOfDay )
			.OrderBy(x => x.SendOrderNum).ToListAsync(cancellationToken);
		var roleId = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];
		if (schedulings.Any())
		{
			var steps = await _workflowDomainService.GetStepsBelongsToAsync(AppDefaults.SendPoolId,
				cancellationToken);
			if (steps.Any())
			{
				List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)> handlers = new();
				var avg = steps.Count / schedulings.Count;
				var remaining = steps.Count % schedulings.Count;
				var skip = 0;
				for (var i = 0; i < schedulings.Count; i++)
				{
					var scheduling = schedulings[i];
					var size = avg + (i < remaining ? 1 : 0);
					if (size > 0)
					{
						var stSteps = steps.Skip(skip).Take(size).ToList();

						handlers.Add(new(
							scheduling.SchedulingUser.UserId,
							scheduling.SchedulingUser.UserName,
							scheduling.SchedulingUser.OrgId,
							scheduling.SchedulingUser.OrgIdName,
							roleId, "派单员",
							stSteps));
						skip += size;
						scheduling.SendOrderNum = scheduling.SendOrderNum.HasValue ? scheduling.SendOrderNum.Value : 0; 
						scheduling.SendOrderNum += size;
						await _schedulingRepository.Updateable()
							.SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum, AdvancedSendOrderNum = size })
							.Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
						await _orderRepository.Updateable().SetColumns(o => new Order()
						{
							CenterToOrgHandlerId = scheduling.SchedulingUser.UserId,
							CenterToOrgHandlerName = scheduling.SchedulingUser.UserName
						})
							.Where(o => stSteps.Any(s => s.ExternalId == o.Id)).ExecuteCommandAsync(cancellationToken);
					}
				}
				if (handlers.Any())
					await _workflowDomainService.ChangeHandlerBatchAsync(handlers, cancellationToken);
			}
		}
	}
	#endregion

	#region  工单校验- 交通类工单

	/// <summary>
	/// 工单校验  - 交通类工单
	/// </summary>
	/// <returns></returns>
	public async Task<OrderValidation> OrderValidation(AddOrderDto dto, CancellationToken cancellationToken)
    {
        var valid = new OrderValidation { Validation = false, Result = "" };
        var hotspot = await _hotspotRepository.GetAsync(dto.HotspotId, cancellationToken);
        if (hotspot != null && hotspot.TrunkNum.Equals(AppDefaults.TrafficTrunkNum))
        {
            switch (dto.AcceptTypeCode)
            {
                //投诉举报
                case "30":
                case "35":
                    valid.Validation = dto.Title.Contains("意见") || dto.Title.Contains("建议") || dto.Title.Contains("咨询")
                        || dto.Content.Contains("意见") || dto.Content.Contains("建议") || dto.Content.Contains("咨询");
                    if (dto.Content.Length < 25)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足,需要至少25个字!";
                    }
                    break;
                // 意见
                case "1":
                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("咨询")
                        || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("咨询");
                    if (dto.Content.Length < 5)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足,需要至少5个字!";
                    }
                    break;
                //建议求助
                case "15":
                case "20":
                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("咨询")
                        || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("咨询");
                    if (dto.Content.Length < 25)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足,需要至少25个字!";
                    }
                    break;
                // 咨询
                case "10":
                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("意见")
                        || dto.Title.Contains("建议") || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("意见") || dto.Content.Contains("建议");
                    if (dto.Content.Length < 5)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足,需要至少5个字!";
                    }
                    break;
                // 表扬
                case "25":
                    if (dto.Content.Length < 25)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足,需要至少25个字!";
                    }
                    break;
                default:
                    if (dto.Content.Length < 5)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足,需要至少5个字!";
                    }
                    break;
            }
        }

        if (valid.Validation && string.IsNullOrEmpty(valid.Result))
            valid.Result = "标题或受理内容出现限制词，请检查!";
        return valid;
    }
    #endregion

    #region 即将超期和超期短信

    /// <summary>
    ///  即将超期和超期短信
    /// </summary>
    /// <returns></returns>
    public async Task SendOverTimeSms(CancellationToken cancellationToken)
    {
        var now = DateTime.Now;
        var isSend = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsSendOverTimeSms).SettingValue[0]);
        if (!isSend)
        {
            return;
        }
        //查询即将超期和超期工单
        var orderList = await _orderRepository.Queryable()
            .Where(x => x.Status < EOrderStatus.Filed && !string.IsNullOrEmpty(x.CurrentHandleOrgId))
            .GroupBy(x => x.CurrentHandleOrgId)
            .Select(x => new OverTimeOrderDto
            {
                OrgId = x.CurrentHandleOrgId,
                NearlyOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(now >= x.NearlyExpiredTime && now < x.ExpiredTime, 1, 0)),
                ExpiredTimeOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.ExpiredTime < now, 1, 0))
            })
            .ToListAsync(cancellationToken);

        foreach (var item in orderList)
        {
            if (item.NearlyOrderCount == 0 && item.ExpiredTimeOrderCount == 0)
            {
                continue;
            }
            var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
            //查询部门所有账号
            var userlist = await _userRepository.Queryable().Where(x =>
                x.OrgId == item.OrgId && !string.IsNullOrEmpty(x.PhoneNo) &&
                x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).ToListAsync();
            //发送短信
            foreach (var user in userlist)
            {
                var messageDto = new Share.Dtos.Push.MessageDto
                {
                    PushBusiness = EPushBusiness.OrderExpire,
                    PushPlatform = EPushPlatform.Sms,
                    Name = user.Name,
                    TemplateCode = "1009",
                    Params = new List<string>() { item.NearlyOrderCount.ToString(), item.ExpiredTimeOrderCount.ToString() },
                    TelNumber = user.PhoneNo,
                };
                await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken);
            }
        }
    }

    #endregion

    #region  计算甄别申请截至日期

    public async Task<DateTime> GetScreenByEndTime()
    {

        var endTime = DateTime.Now;
        var beginTime = DateTime.Now;
        var timeValue = 1;
        if (_appOptions.Value.IsZiGong)
        {
            timeValue = 2;
        }
        else if (_appOptions.Value.IsLuZhou)
        {
            DateTime firstDayOfMonth = new DateTime(beginTime.Year, beginTime.Month, 1);
            DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
            beginTime = new DateTime(lastDayOfMonth.Year, lastDayOfMonth.Month, lastDayOfMonth.Day, 23, 59, 59);
        }
        else
        {
            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ScreenApplyEndTime);
            if (int.Parse(setting?.SettingValue[0]) > 0)
            {
                timeValue = int.Parse(setting?.SettingValue[0]);
            }
        }
        endTime = (await _expireTime.CalcEndTime(beginTime, beginTime, ETimeType.WorkDay, timeValue, 0, 0)).EndTime;
        return endTime;
    }
    #endregion


	#region private

	private async Task<Order> GetOrderByFlowIdAsync(string workflowId, CancellationToken cancellationToken)
    {
        if (string.IsNullOrEmpty(workflowId))
            throw UserFriendlyException.SameMessage("无效流程编号");
        var order = await _orderRepository.Queryable()
            .Includes(d => d.Hotspot)
            .FirstAsync(d => d.WorkflowId == workflowId);
        if (order == null)
            throw new UserFriendlyException($"无效流程编号, workflowId: {workflowId}", "无效流程编号");
        return order;
    }

    private CacheOrderNO GetCurrentOrderNo()
    {
        var today = DateTime.Today;
        var cacheKey = today.ToString("yyyyMMdd");
        var cacheOrderNo = _cacheOrderNo.GetOrSet(cacheKey, f =>
        {
            var todayOrderCount = _orderRepository.Queryable(includeDeleted: true)
                .CountAsync(d => d.CreationTime.Date == today.Date)
                .GetAwaiter()
                .GetResult();
            return new CacheOrderNO { TotalCount = todayOrderCount };
        }, TimeSpan.FromDays(1));

        return cacheOrderNo;
    }

    public string GenerateNewOrderNo()
    {
        var today = DateTime.Today;
        var cacheKey = $"{today:yyyyMMdd}";
        var cacheOrderNo = _cacheOrderNo.GetOrSet(cacheKey, f =>
        {
            var todayOrderCount = _orderRepository.Queryable(true)
                .CountAsync(d => d.CreationTime.Date == today.Date)
                .GetAwaiter()
                .GetResult();
            return new CacheOrderNO { TotalCount = todayOrderCount };
        }, TimeSpan.FromDays(1));
        cacheOrderNo!.TotalCount += 1;
        var no = GenerateOrderNo(today, cacheOrderNo.TotalCount);
        _cacheOrderNo.Set(cacheKey, cacheOrderNo);
        return no;
    }

    private string GenerateNewProvinceNo(string no, string sourceChannelCode)
    {
        //诉求渠道+成员单位标识+行政区划代码+年月日+流水号
        //成员单位标识  99
        //宜宾市 511500  市级
        var today = DateTime.Today;
        //var count = no.Substring(no.Length - 5);
        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.VersionsAreaCode);
        var versionsAreaCode = setting?.SettingValue[0];

        //todo 双系统并行暂时执行此方案
        var count = no.Substring(no.Length - 4);
        count = (Convert.ToInt32(count) + 50000).ToString();

        var provinceCodes = new[] { "RGDH", "WX", "WB", "AP", "WZ", "YJ", "SCZWFWW", "XCX", "QT" };
        var prefix = provinceCodes.Any(d => d.Equals(sourceChannelCode))
            ? sourceChannelCode
            : "QT";
        return $"{prefix}99{versionsAreaCode}{today:yyMMdd}{count}";
    }


    private string GenerateOrderNo(DateTime today, int count)
    {
        return $"{today:yyyyMMdd}{count:000000}";
    }

    /// <summary>
    /// 根据 OrderId 返回用户电话评价枚举
    /// 默认返回 默认满意
    /// </summary>
    /// <param name="orderId"></param>
    /// <returns></returns>
    private async Task<EVoiceEvaluate> GetReplyVoiceOrDefaultByOrderIdAsync(string orderId)
    {
        var callNative = await _callDomainService.GetByOrderIdAsync(orderId);
        if (callNative is null || callNative.ReplyTxt.IsNullOrEmpty())
            return EVoiceEvaluate.DefaultSatisfied;

        try
        {
            var smsReply = _orderVisitDomainService.GetVisitEvaluateByReplyTxt(callNative.ReplyTxt!.Trim());
            return smsReply!.VoiceEvaluate!.Value;
        }
        catch (UserFriendlyException)
        {
            return EVoiceEvaluate.DefaultSatisfied;
        }
    }
    #endregion
}

public class CacheOrderNO
{
    public int TotalCount { get; set; }
}

public class OrderValidation
{
    public bool Validation { get; set; }

    public string Result { get; set; }

}