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;

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 IMapper _mapper;
    private readonly ILogger<OrderDomainService> _logger;
    private readonly IFileRepository _fileRepository;
    private readonly IRepository<Scheduling> _schedulingRepository;
    private readonly IRepository<User> _userRepository;
    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
    private readonly IWorkflowDomainService _workflowDomainService;
    private readonly IRepository<Hotspot> _hotspotRepository;


    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)
    {
        _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;
    }

    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();
        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();
        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();
        complement.IsProComplement = true;
        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 平均派单
    /// <summary>
    /// 平均派单
    /// </summary>
    /// <returns></returns>
    public async Task<FlowStepHandler> 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);
        if (scheduling is null)
            return new FlowStepHandler
            {
                Key = AppDefaults.SendPoolId,
                Value = "待派单池",
                UserId = AppDefaults.SendPoolId,
                Username = "待派单池",
                OrgId = OrgSeedData.CenterId,
                OrgName = "市民热线服务中心"
            };
        scheduling.SendOrderNum++;
        await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);
        var user = scheduling.SchedulingUser;
        return new FlowStepHandler
        {
            Key = user.UserId,
            Value = user.UserName,
            UserId = user.UserId,
            Username = user.UserName,
            OrgId = user.OrgId,
            OrgName = user.OrgIdName
        };
    }

    /// <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 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;
            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 _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,null,null, sendSteps)
            }, cancellationToken);
        }

    }

    /// <summary>
    /// 触发平均派单
    /// </summary>
    /// <returns></returns>
    public async Task TriggerAverageOrder(CancellationToken cancellationToken)
    {
        //1.从排班里面获取今天上班的人
        //2.获取默认派单员剩下的工单
        //3.平均分配剩下的工单给今天上班的人
        DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
        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)
                    {
						handlers.Add(new(
							scheduling.SchedulingUser.UserId,
							scheduling.SchedulingUser.UserName,
							scheduling.SchedulingUser.OrgId,
							scheduling.SchedulingUser.OrgIdName,
							null, null,
							steps.Skip(skip).Take(size).ToList()));
                        skip += size;
						scheduling.SendOrderNum += size;
						await _schedulingRepository.Updateable()
							.SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum })
							.Where(s => s.Id == scheduling.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.TrunkNum.Equals(AppDefaults.TrafficTrunkNum))
        {
            switch (dto.AcceptTypeCode)
            {
                //投诉举报
                case "30":
                case "35":
                    valid.Validation = dto.Title.Contains("意见") || dto.Title.Contains("建议") || dto.Title.Contains("信息") || dto.Title.Contains("咨询");
                    valid.Validation = dto.Content.Contains("意见") || dto.Content.Contains("建议") || dto.Content.Contains("信息") || dto.Content.Contains("咨询");
                    if (dto.Content.Length < 25)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足!";
                    }
                    break;
                // 意见
                case "1":
                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("信息") || dto.Title.Contains("咨询");
                    valid.Validation = dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("信息") || dto.Content.Contains("咨询");
                    if (dto.Content.Length < 5)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足!";
                    }
                    break;
                //建议求助
                case "15":
                case "20":
                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("信息") || dto.Title.Contains("咨询");
                    valid.Validation = dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("信息") || dto.Content.Contains("咨询");
                    if (dto.Content.Length < 25)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足!";
                    }
                    break;
                // 咨询
                case "10":
                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("意见") || dto.Title.Contains("建议");
                    valid.Validation = dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("意见") || dto.Content.Contains("建议");
                    if (dto.Content.Length < 5)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足!";
                    }
                    break;
                // 表扬
                case "25":
                    if (dto.Content.Length < 25)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足!";
                    }
                    break;
                default:
                    if (dto.Content.Length < 5)
                    {
                        valid.Validation = true;
                        valid.Result = "保存失败，受理内容字数不足!";
                    }
                    break;
            }
        }

        if (valid.Validation && string.IsNullOrEmpty(valid.Result))
            valid.Result = "标题或受理内容出现限制词，请检查!";
        return valid;
    }
    #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;
    }

    private 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}";
    }

    #endregion
}

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

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

    public string Result { get; set; }

}