using Hotline.Caching.Interfaces;
using Hotline.Settings.TimeLimits;
using Hotline.Share.Dtos.Settings;
using Hotline.Share.Enums.FlowEngine;
using Hotline.Share.Enums.Settings;
using Hotline.Share.Tools;
using Mapster;
using MapsterMapper;
using XF.Domain.Exceptions;
using XF.Domain.Repository;
using XF.Utility.EnumExtensions;

namespace Hotline.Settings.TimeLimitDomain;

/// <summary>
/// 过期时间处理基类
/// </summary>
public abstract class ExpireTimeLimitBase
{
    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
    private readonly IRepository<TimeLimitSetting> _timeLimitSettingRepository;
    private readonly IExpireTimeHandler _expireTimeHandler;
    private readonly IMapper _mapper;
    private readonly IDaySettingCacheManager _daySettingCacheManager;

    public ExpireTimeLimitBase(ISystemSettingCacheManager systemSettingCacheManager, IRepository<TimeLimitSetting> timeLimitSettingRepository, IExpireTimeHandler expireTimeHandler, IMapper mapper,  IDaySettingCacheManager daySettingCacheManager)
    {
        _systemSettingCacheManager = systemSettingCacheManager;
        _timeLimitSettingRepository = timeLimitSettingRepository;
        _expireTimeHandler = expireTimeHandler;
        _mapper = mapper;
        _daySettingCacheManager = daySettingCacheManager;
    }

    public virtual async Task<int> CalcWorkTime(DateTime beginTime, DateTime endTime, bool isCenter)
    {
        // var workTime = _systemSettingRepository.Get(x => x.Code == SettingConstants.WorkTime);
        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        return await _expireTimeHandler.CalcWorkTimeAsync(beginTime, endTime, workTime, isCenter);
    }

    public virtual async Task<int> CalcWorkTimeEx(DateTime beginTime, DateTime endTime, bool isCenter)
    {
        // var workTime = _systemSettingRepository.Get(x => x.Code == SettingConstants.WorkTimeOrg);
        var workTime = GetWorkTimes(SettingConstants.WorkTimeOrg);
        return await _expireTimeHandler.CalcWorkTimeExAsync(beginTime, endTime, workTime, isCenter);
    }

    /// <summary>
    /// 判断当前时间是否是工作日 返回一个工作日
    /// </summary>
    /// <param name="date"></param>
    /// <returns></returns>
    public virtual async Task<DateTime> WorkDay(DateTime date)
    {
        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        var (WorkBeginTime, WorkEndTime) = GetWorkTime(DateTime.Now, workTime);
        if (await IsWorkDay(date))
        {
            if (date < WorkBeginTime || date > WorkEndTime)
            {
                date = DateTime.Parse(date.ToShortDateString() + " " + workTime[0] + ":00");
            }
        }
        else
        {
            while (await NotWorkDay(date))
            {
                date = date.AddDays(1);
            }
            if (date < WorkBeginTime || date > WorkEndTime)
            {
                date = DateTime.Parse(date.ToShortDateString() + " " + workTime[0] + ":00");
            }
        }
        return date;
    }

    public virtual async Task<DateTime> GetWorkDay(DateTime date)
    {
	 
		if (await IsWorkDay(date))
		{
			var workTime = GetWorkTimes(SettingConstants.WorkTime);
			var (WorkBeginTime, WorkEndTime) = GetWorkTime(date, workTime);
            if (date > WorkBeginTime && date < WorkEndTime)
            {
                date = date.AddDays(1);
				while (await NotWorkDay(date))
				{
					date = date.AddDays(1);
				}
			}
            else {
				date = WorkBeginTime.AddDays(2);
				while (await NotWorkDay(date))
				{
					date = date.AddDays(1);
				}
			}
	    }
	    else
	    {
		    while (await NotWorkDay(date))
		    {
			    date = date.AddDays(1);
		    }
			date = DateTime.Parse(date.ToShortDateString() + " 18:00");
		}
	    return date;
    }


	public async Task<DateTime?> CalcWorkTimeReduce(DateTime now, int timeValue)
    {
        return await _expireTimeHandler.CalcWorkTimeReduceAsync(now, timeValue);
    }

    /// <summary>
    /// 获取办理时限配置
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    public TimeConfig GetOrderTimeLimitConfigBase(string? code = null)
    {
        if (string.IsNullOrEmpty(code))
        {
            return new TimeConfig(
                int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.BackCenterTimeSetting)!.SettingValue[0]),
                ETimeType.WorkDay);
        }
        else
        {
            return _timeLimitSettingRepository.Queryable().First(x => x.BusCode == code).Adapt<TimeConfig>();
        }
    }

    /// <summary>
    /// 获取办理时限配置
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    public virtual TimeConfig GetOrderTimeLimitConfig(string? code = null)
    {
        return GetOrderTimeLimitConfigBase(code);
    }


    /// <summary>
    /// 计算期满时间
    /// </summary>
   // public virtual async Task<ExpiredTimeWithConfig> CalcExpiredTime(DateTime beginTime,DateTime startTime, EFlowDirection flowDirection, OrderTimeClacInfo order)
   // {
   //     if (EFlowDirection.CenterToOrg == flowDirection && order.Is24HoursComplete)
   //     {
   //         order.AcceptTypeCode = "24";
   //     }

   //     if (flowDirection is EFlowDirection.CenterToOrg && string.IsNullOrEmpty(order.AcceptTypeCode))
   //         throw new UserFriendlyException("中心派至部门的工单期满时间需受理类型参数");
   //     var timeConfig = flowDirection switch
   //     {
   //         EFlowDirection.CenterToOrg => GetOrderTimeLimitConfigBase(order.AcceptTypeCode),
   //         EFlowDirection.OrgToCenter => GetOrderTimeLimitConfigBase(),
   //         EFlowDirection.FiledToCenter => GetOrderTimeLimitConfigBase(),
			//EFlowDirection.CenterToCenter => GetOrderTimeLimitConfigBase(order.AcceptTypeCode),
   //         EFlowDirection.FiledToOrg => GetOrderTimeLimitConfigBase(order.AcceptTypeCode),
			//_ => throw new ArgumentOutOfRangeException(nameof(flowDirection), flowDirection, null)
   //     };

   //     return await CalcEndTime(beginTime,startTime, timeConfig, order.AcceptTypeCode);
   // }

    public virtual async Task<ExpiredTimeWithConfig> CalcExpiredTime(DateTime beginTime,DateTime startTime,EFlowDirection flowDirection,OrderTimeClacInfo order)
    {
        if (EFlowDirection.CenterToOrg == flowDirection && order.Is24HoursComplete)
        {
            order.AcceptTypeCode = "24";
        }

        if (flowDirection is EFlowDirection.CenterToOrg && string.IsNullOrEmpty(order.AcceptTypeCode))
            throw new UserFriendlyException("中心派至部门的工单期满时间需受理类型参数");
        var timeConfig = flowDirection switch
        {
            EFlowDirection.CenterToOrg => GetOrderTimeLimitConfigBase(order.AcceptTypeCode),
            EFlowDirection.OrgToCenter => GetOrderTimeLimitConfigBase(),
            EFlowDirection.FiledToCenter => GetOrderTimeLimitConfigBase(),
            EFlowDirection.CenterToCenter => GetOrderTimeLimitConfigBase(order.AcceptTypeCode),
            EFlowDirection.FiledToOrg => GetOrderTimeLimitConfigBase(order.AcceptTypeCode),
            _ => throw new ArgumentOutOfRangeException(nameof(flowDirection), flowDirection, null)
        };

        return await CalcEndTime(beginTime,startTime, timeConfig, order.AcceptTypeCode);
    }


    //public virtual async Task<ExpiredTimeWithConfig> CalcEndTime(DateTime beginTime,DateTime startTime, TimeConfig timeConfig, string busCode)
    //{
    //    var setting = _timeLimitSettingRepository.Queryable().First(x => x.BusCode == busCode)
    //        ?? throw new UserFriendlyException($"缺少busCode:{busCode}, 的time limit setting 设置;");

    //    var workTime = GetWorkTimes(SettingConstants.WorkTime);
    //    var result = await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, timeConfig.TimeType, workTime, timeConfig.Count, setting.Percentage, setting.PercentageOne);
    //    var expiredTimeWithConfig = timeConfig.Adapt<ExpiredTimeWithConfig>();
    //    return result.Adapt(expiredTimeWithConfig);
    //}

    public virtual async Task<ExpiredTimeWithConfig> CalcEndTime(DateTime beginTime,DateTime startTime,TimeConfig timeConfig,string busCode)
    {
        var setting = _timeLimitSettingRepository.Queryable().First(x => x.BusCode == busCode)
            ?? throw new UserFriendlyException($"缺少busCode:{busCode}, 的time limit setting 设置;");

        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        var result = await _expireTimeHandler.CalcEndTimeNewAsync(beginTime,startTime, timeConfig.TimeType, workTime, timeConfig.Count, setting.Percentage, setting.PercentageOne,setting.EndType);
        var expiredTimeWithConfig = timeConfig.Adapt<ExpiredTimeWithConfig>();
        return result.Adapt(expiredTimeWithConfig);
    }

    /// <summary>
    /// 计算工作日
    /// </summary>
    /// <param name="beginTime"></param>
    /// <param name="busCode"></param>
    /// <param name="isCenter"></param>
    /// <returns></returns>
    public virtual async Task<ExpiredTimeWithConfig?> CalcEndTime(DateTime beginTime,DateTime startTime, string busCode)
    {
        var setting = _timeLimitSettingRepository.Queryable().First(x => x.BusCode == busCode);
        if (setting == null) return null;

        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        var result = await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, workTime,setting);
        var expiredTimeWithConfig = new ExpiredTimeWithConfig
        {
            TimeText = setting.TimeValue + "个" + setting.TimeType.GetDescription(),
            TimeType = setting.TimeType,
            Count = setting.TimeValue,
            ExpiredTime = result.EndTime,
            NearlyExpiredTime = result.NearlyExpiredTime,
            NearlyExpiredTimeOne = result.NearlyExpiredTimeOne
        };
        return expiredTimeWithConfig;
    }


    public virtual async Task<TimeResult?> CalcEndTime(DateTime beginTime,DateTime startTime, ETimeType timeType, int timeValue, string busCode)
    {
        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        var setting = _timeLimitSettingRepository.Queryable().First(x => x.BusCode == busCode);
        if (setting == null) return null;
		if (timeValue > 0) setting.TimeValue = timeValue;
        return await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime, workTime,setting);
    }

    /// <summary>
    /// 计算工作日
    /// </summary>
    /// <param name="beginTime"></param>
    /// <param name="timeType"></param>
    /// <param name="timeValue"></param>
    /// <param name="Percentage">即将超期百分比</param>
    /// <param name="PercentageOne">超期百分比第一级</param>
    /// <returns></returns>    
    public virtual async Task<TimeResult> CalcEndTime(DateTime beginTime,DateTime startTime, ETimeType timeType, int timeValue, int Percentage, int PercentageOne)
    {
        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        return await _expireTimeHandler.CalcEndTimeAsync(beginTime,startTime,  workTime, new TimeLimitSetting() { TimeType = timeType,TimeValue = timeValue,Percentage=Percentage,PercentageOne=PercentageOne });
    }

    /// <summary>
    /// 计算工作时间小时
    /// </summary>
    /// <param name="beginTime"></param>
    /// <param name="endTime"></param>
    /// <param name="isCenter"></param>
    /// <returns></returns>
    public virtual async Task<decimal> CalcWorkTimeToHour(DateTime beginTime, DateTime endTime, bool isCenter)
    {
        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        var min = await _expireTimeHandler.CalcWorkTimeAsync(beginTime, endTime, workTime, isCenter);
        if (min == 0) return 0;
        if (isCenter)
        {
            return Math.Round((decimal)min / 60 / 60 / 24, 2);
        }
        else
        {
            if (workTime == null) return 0;
            return Math.Round((decimal)min / 60 / 60, 2);
        }
    }

    /// <summary>
    /// 计算工作时间天数
    /// </summary>
    /// <param name="beginTime"></param>
    /// <param name="endTime"></param>
    /// <param name="isCenter"></param>
    /// <returns></returns>
    public virtual async Task<decimal> CalcWorkTimeToDecimal(DateTime beginTime, DateTime endTime, bool isCenter)
    {
        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        var min = await _expireTimeHandler.CalcWorkTimeAsync(beginTime, endTime, workTime, isCenter);
        if (min == 0) return 0;
        if (isCenter) return Math.Round((decimal)min / 60 / 60 / 24, 2);

        if (workTime == null) return 0;

        var (workBeginTime, workEndTime) = GetWorkTime(beginTime, workTime);
        var minuteSpan = new TimeSpan(workEndTime.Ticks - workBeginTime.Ticks);

        //总时差分钟数
        int minutes = (int)minuteSpan.TotalMinutes;
        return Math.Round(((decimal)min / 60) / minutes, 2);
    }

    public virtual async Task<TimeResult?> CalcEndTime(EFlowDirection flowDirection, int Percentage, int PercentageOne, ETimeType? timeType = null, int? timeValue = null)
    {
        var workTime = GetWorkTimes(SettingConstants.WorkTime);
        switch (flowDirection)
        {
            case EFlowDirection.CenterToOrg:
                if (!timeType.HasValue || !timeValue.HasValue)
                    throw new UserFriendlyException("无效参数");
                return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now,DateTime.Now, workTime, new TimeLimitSetting() { TimeType = timeType.Value, TimeValue = timeValue.Value, Percentage = Percentage, PercentageOne = PercentageOne });
            case EFlowDirection.OrgToCenter:
                //todo 根据配置
                //return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now,DateTime.Now, ETimeType.Day, workTime, 1, Percentage, PercentageOne);
                return await _expireTimeHandler.CalcEndTimeAsync(DateTime.Now, DateTime.Now, workTime, new TimeLimitSetting() { TimeType =  ETimeType.Day, TimeValue = 1, Percentage = Percentage, PercentageOne = PercentageOne });
            default:
                throw new ArgumentOutOfRangeException(nameof(flowDirection), flowDirection, null);
        }
    }

    /// <summary>
    /// 是工作日
    /// 根据数据库的工作日和周末时间判断
    /// </summary>
    /// <param name="date"></param>
    /// <returns></returns>
    private async Task<bool> IsWorkDay(DateTime date) =>
        _daySettingCacheManager.IsWorkDay(date);

    /// <summary>
    /// 非工作日
    /// 根据数据库的工作日和周末时间判断
    /// </summary>
    /// <param name="date"></param>
    /// <returns></returns>
    private async Task<bool> NotWorkDay(DateTime date)
        => !await IsWorkDay(date);

    /// <summary>
    /// 获取给定日期的工作时间
    /// </summary>
    /// <param name="date"></param>
    /// <param name="workTime"></param>
    /// <returns></returns>
    private static (DateTime startTime, DateTime endTime) GetWorkTime(DateTime date, IList<string> workTime)
    {
        var startTime = DateTime.Parse(date.ToShortDateString() + " " + workTime[0] + ":00");
        var endTime = DateTime.Parse(date.ToShortDateString() + " " + workTime[1] + ":00");
        return (startTime, endTime);
    }

    public IList<string> GetWorkTimes(string code)
        => _systemSettingCacheManager.GetSetting(code)!.SettingValue;

}
