using Hotline.Application.StatisticalReport;
using Hotline.Caching.Interfaces;
using Hotline.CallCenter.Calls;
using Hotline.CallCenter.Tels;
using Hotline.Repository.SqlSugar.Extensions;
using Hotline.Settings;
using Hotline.Share.Dtos;
using Hotline.Share.Dtos.CallCenter;
using Hotline.Share.Dtos.Order;
using Hotline.Share.Dtos.TrCallCenter;
using Hotline.Share.Enums.CallCenter;
using Hotline.Share.Enums.User;
using Hotline.Share.Requests;
using Hotline.Tools;
using Hotline.Users;
using MapsterMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NPOI.SS.Formula.Functions;
using SqlSugar;
using System.Data;
using XF.Domain.Exceptions;
using XF.Domain.Repository;

namespace Hotline.Api.Controllers.Bi;

/// <summary>
/// 话务报表
/// </summary>
public class BiCallController : BaseController
{
    private readonly IRepository<TrCallRecord> _trCallRecordRepository;
    private readonly IRepository<User> _userRepository;
    private readonly IRepository<TelRest> _telRestRepository;
    private readonly IMapper _mapper;
    private readonly ITrCallRecordRepository _trCallRecordRepositoryEx;
    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
    private readonly IRepository<Work> _workRepository;
    private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
    private readonly ICallReportApplication _callReportApplication;



    public BiCallController(
        IRepository<TrCallRecord> trCallRecordRepository,
        IRepository<User> userRepository,
        IRepository<TelRest> telRestRepository,
        IMapper mapper,
        ITrCallRecordRepository trCallRecordRepositoryEx,
        ISystemSettingCacheManager systemSettingCacheManager,
        ISystemDicDataCacheManager sysDicDataCacheManager,
        IRepository<Work> workRepository,
        ICallReportApplication callReportApplication)
    {
        _trCallRecordRepository = trCallRecordRepository;
        _userRepository = userRepository;
        _telRestRepository = telRestRepository;
        _mapper = mapper;
        _trCallRecordRepositoryEx = trCallRecordRepositoryEx;
        _systemSettingCacheManager = systemSettingCacheManager;
        _workRepository = workRepository;
        _sysDicDataCacheManager = sysDicDataCacheManager;
        _callReportApplication = callReportApplication;

    }

    /// <summary>
    /// 话务统计分析
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("calls")]
    [AllowAnonymous]
    public async Task<List<BiCallDto>> QueryCallsAsync([FromQuery] BiQueryCallsDto dto)
    {
        if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
        #region 注释
        //var items = await _trCallRecordRepository.Queryable()
        //    .Where(d => d.CreatedTime >= dto.StartTime && d.CreatedTime <= dto.EndTime)
        //    .Where(d => d.CallDirection == ECallDirection.In)
        //    .WhereIF(!string.IsNullOrEmpty(dto.Line), d => d.Gateway == dto.Line)
        //    .Select(d => new
        //    {
        //        d.CreatedTime.Year,
        //        d.CreatedTime.Month,
        //        d.CreatedTime.Day,
        //        d.CreatedTime.Hour,
        //        d.AnsweredTime,
        //        d.EndBy
        //    })
        //    .MergeTable()
        //    .GroupBy(d => new
        //    {
        //        d.Year,
        //        d.Month,
        //        d.Day,
        //        d.Hour
        //    })
        //    .Select(d => new BiCallDto
        //    {
        //        Hour = d.Hour,
        //        Total = SqlFunc.AggregateCount(d.Hour),
        //        Answered = SqlFunc.AggregateSum(SqlFunc.IIF(d.AnsweredTime != null, 1, 0)),
        //        Hanguped = SqlFunc.AggregateSum(SqlFunc.IIF(d.AnsweredTime == null && d.EndBy != null && d.EndBy.Value == EEndBy.To, 1, 0))
        //    })
        //    .OrderBy(d => d.Hour)
        //    .ToListAsync(HttpContext.RequestAborted);

        //if (items.Count < 24)
        //{
        //    for (var i = 0; i < 24; i++)
        //    {
        //        var item = items.FirstOrDefault(d => d.Hour == i);
        //        if (item is null)
        //            items.Add(new BiCallDto { Hour = i });
        //    }

        //    items = items.OrderBy(d => d.Hour).ToList();
        //}
        #endregion
        dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);

        return await _trCallRecordRepositoryEx.GetQueryCalls(dto.StartTime.Value, dto.EndTime.Value, dto.Keyword);

    }

    /// <summary>
    /// 话务统计分析---导出
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("calls_export")]
    [AllowAnonymous]
    public async Task<FileStreamResult> ExportQueryCallsAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
    {
        if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");

        dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);

        var list = await _trCallRecordRepositoryEx.GetQueryCalls(dto.QueryDto.StartTime.Value, dto.QueryDto.EndTime.Value, dto.QueryDto.Keyword);

        if (list != null && list.Count > 0)
        {
            list.Add(new BiCallDto()
            {
                HourRange = "合计",
                Hour = 13,
                Total = list.Sum(p => p.Total),
                Answered = list.Sum(p => p.Answered),
                Hanguped = list.Sum(p => p.Hanguped)
            });
        }


        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);

        var dtos = list
            .Select(stu => _mapper.Map(stu, typeof(BiCallDto), dynamicClass))
            .Cast<object>()
            .ToList();

        var stream = ExcelHelper.CreateStream(dtos);

        return ExcelStreamResult(stream, "话务统计分析");
    }

    /// <summary>
    /// 话务日期明细
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("query_calls_detail")]
    public async Task<object> QueryCallsDetailAsync([FromQuery] BiQueryCallsDto dto)
    {
        if (!dto.StartTime.HasValue || !dto.EndTime.HasValue)
            throw UserFriendlyException.SameMessage("请选择时间!");
        dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);

        var items = await _callReportApplication.QueryCallsDetailAsync(dto);

        var total = new QueryCallsDetailDto
        {
            Date = "合计",
            Hour = "",
            InTotal = items.Sum(p => p.InTotal),
            InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
            NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
            TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
            InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
            InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
            TimeoutConnection = items.Sum(p => p.TimeoutConnection),
            TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
            QueueByeCount = items.Sum(p => p.QueueByeCount),
            IvrByeCount = items.Sum(p => p.IvrByeCount),
            OutTotal = items.Sum(p => p.OutTotal),
            OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
        };

        return new { List = items, Total = total };

    }

    /// <summary>
    /// 话务日期明细--导出
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("query_calls_detail_export")]
    public async Task<FileStreamResult> QueryCallsDetailExportAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
    {
        if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue)
            throw UserFriendlyException.SameMessage("请选择时间!");
        dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);

        var items = await _callReportApplication.QueryCallsDetailAsync(dto.QueryDto);

        var total = new QueryCallsDetailDto
        {
            Date = "合计",
            Hour = "",
            InTotal = items.Sum(p => p.InTotal),
            InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
            NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
            TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
            InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
            InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
            TimeoutConnection = items.Sum(p => p.TimeoutConnection),
            TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
            QueueByeCount = items.Sum(p => p.QueueByeCount),
            IvrByeCount = items.Sum(p => p.IvrByeCount),
            OutTotal = items.Sum(p => p.OutTotal),
            OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
        };
        items.Add(total);

        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);

        var dtos = items
            .Select(stu => _mapper.Map(stu, typeof(QueryCallsDetailDto), dynamicClass))
            .Cast<object>()
            .ToList();

        var stream = ExcelHelper.CreateStream(dtos);

        return ExcelStreamResult(stream, "话务日期明细数据");

    }

    /// <summary>
    /// 话务日期明细--呼入明细
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("query_incall_calls_list")]
    public async Task<PagedDto<TrCallDtoNew>> GetInCallCallListAsync([FromQuery] BiQueryCallsDto dto)
    {
        var (total, items) = await _callReportApplication.QueryCallsDetailInTotalAsync(dto)
            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);

        return new PagedDto<TrCallDtoNew>(total, _mapper.Map<IReadOnlyList<TrCallDtoNew>>(items));
    }

    /// <summary>
    /// 话务日期明细----导出
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("query_incall_calls_list_export")]
    public async Task<FileStreamResult> GetInCallCallListExportAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
    {
        var query = _callReportApplication.QueryCallsDetailInTotalAsync(dto.QueryDto);
        List<TrCallRecord> data;
        if (dto.IsExportAll)
        {
            data = await query.ToListAsync(HttpContext.RequestAborted);
        }
        else
        {
            var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
            data = items;
        }

        var dataDtos = _mapper.Map<ICollection<TrCallDtoNew>>(data);

        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);

        var dtos = dataDtos
            .Select(stu => _mapper.Map(stu, typeof(TrCallDtoNew), dynamicClass))
            .Cast<object>()
            .ToList();

        var stream = ExcelHelper.CreateStream(dtos);

        string name = dto.QueryDto.TypeCode == "2" ? "话务日期-接通明细数据" : "话务日期-总量明细数据";

        return ExcelStreamResult(stream, name);

    }

    /// <summary>
    /// 话务日期明细--时间段
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("query_calls_hour_detail_list")]
    public async Task<object> QueryCallsHourDetailListAsync([FromQuery] BiQueryCallsDto dto)
    {
        if (!dto.StartTime.HasValue || !dto.EndTime.HasValue)
            throw UserFriendlyException.SameMessage("请选择时间!");
        dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);
        //超时接通量
        int CallInOverConnRingTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.CallInOverConnRingTime)?.SettingValue[0]);
        //坐席超时挂断时间
        int SeatChaoTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SeatChaoTime)?.SettingValue[0]);

        //未接秒挂时间
        int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
        //呼入有效时间
        int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
        //接通秒挂时间
        int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);

        var items = await _trCallRecordRepositoryEx.QueryCallsHourDetail(dto.StartTime.Value, dto.EndTime.Value, noConnectByeTimes, effectiveTimes
              , connectByeTimes, CallInOverConnRingTime, SeatChaoTime, dto.Keyword);

        var total = new QueryCallsDetailDto
        {
            Date = "",
            Hour = "合计",
            InTotal = items.Sum(p => p.InTotal),
            InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
            NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
            TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
            InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
            InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
            TimeoutConnection = items.Sum(p => p.TimeoutConnection),
            TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
            QueueByeCount = items.Sum(p => p.QueueByeCount),
            IvrByeCount = items.Sum(p => p.IvrByeCount),
            OutTotal = items.Sum(p => p.OutTotal),
            OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
        };

        return new { List = items, Total = total };

    }

    /// <summary>
    /// 话务日期明细--时间段--导出
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("query_calls_hour_detail_list_export")]
    public async Task<FileStreamResult> QueryCallsHourDetailListExportAsync([FromBody] ExportExcelDto<BiQueryCallsDto> dto)
    {
        if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue)
            throw UserFriendlyException.SameMessage("请选择时间!");
        dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);
        //超时接通量
        int CallInOverConnRingTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.CallInOverConnRingTime)?.SettingValue[0]);
        //坐席超时挂断时间
        int SeatChaoTime = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SeatChaoTime)?.SettingValue[0]);

        //未接秒挂时间
        int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
        //呼入有效时间
        int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
        //接通秒挂时间
        int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);

        var items = await _trCallRecordRepositoryEx.QueryCallsHourDetail(dto.QueryDto.StartTime.Value, dto.QueryDto.EndTime.Value, noConnectByeTimes, effectiveTimes
              , connectByeTimes, CallInOverConnRingTime, SeatChaoTime, dto.QueryDto.Keyword);

        var total = new QueryCallsDetailDto
        {
            Date = "",
            Hour = "合计",
            InTotal = items.Sum(p => p.InTotal),
            InConnectionQuantity = items.Sum(p => p.InConnectionQuantity),
            NotAcceptedHang = items.Sum(p => p.NotAcceptedHang),
            TotalDurationIncomingCalls = items.Sum(p => p.TotalDurationIncomingCalls),
            InAvailableAnswer = items.Sum(p => p.InAvailableAnswer),
            InHangupImmediateWhenAnswered = items.Sum(p => p.InHangupImmediateWhenAnswered),
            TimeoutConnection = items.Sum(p => p.TimeoutConnection),
            TimeoutSuspension = items.Sum(p => p.TimeoutSuspension),
            QueueByeCount = items.Sum(p => p.QueueByeCount),
            IvrByeCount = items.Sum(p => p.IvrByeCount),
            OutTotal = items.Sum(p => p.OutTotal),
            OutConnectionQuantity = items.Sum(p => p.OutConnectionQuantity)
        };

        items.Add(total);

        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);

        var dtos = items
            .Select(stu => _mapper.Map(stu, typeof(QueryCallsDetailDto), dynamicClass))
            .Cast<object>()
            .ToList();

        var stream = ExcelHelper.CreateStream(dtos);

        return ExcelStreamResult(stream, "话务日期明细-时间段");

    }

    /// <summary>
    /// 坐席话务统计分析
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("seats/export")]
    public async Task<FileStreamResult> ExportSeatss([FromBody] ExportExcelDto<ReportPagedRequest> dto)
    {
        if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
        dto.QueryDto.EndTime = dto.QueryDto.EndTime.Value.AddDays(1).AddSeconds(-1);

        var list =  await _callReportApplication.QuerySeatCallAsync(dto.QueryDto);
        if (list != null && list.Count > 0)
        {
            list.Add(new BiSeatCallsDto()
            {
                Name = "合计",
                InTotal = list.Sum(p => p.InTotal),
                InAnswered = list.Sum(p => p.InAnswered),
	        InHangupImmediate = list.Sum(p => p.InHangupImmediate),
                InHanguped = list.Sum(m => m.InHanguped),
                OutDurationAvg = list.Sum(m => m.OutDurationAvg),
                InAvailableAnswer = list.Sum(m => m.InAvailableAnswer),
                InHangupImmediateWhenAnswered = list.Sum(m => m.InHangupImmediateWhenAnswered),
                OutTotal = list.Sum(m => m.OutTotal),
                OutAnswered = list.Sum(m => m.OutAnswered),
                LoginDuration = list.Sum(m => m.LoginDuration),
                RestDuration = list.Sum(m => m.RestDuration),
                InDurationAvg = list.Sum(m => m.InDurationAvg)
            });
        }

        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);

        var dtos = list
            .Select(stu => _mapper.Map(stu, typeof(BiSeatCallsDto), dynamicClass))
            .Cast<object>()
            .ToList();

        var stream = ExcelHelper.CreateStream(dtos);

        return ExcelStreamResult(stream, "坐席话务统计分析");
    }

    /// <summary>
    /// 坐席话务统计分析
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("seats")]
    [AllowAnonymous]
    public async Task<IReadOnlyList<BiSeatCallsDto>> QuerySeatCallsAsync([FromQuery] ReportPagedRequest dto)
    {
        if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
        dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);

        //获取配置
        int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
        int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
        int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);

        var list = await _userRepository.Queryable()
              .LeftJoin<TrCallRecord>((u, c) => u.Id == c.UserId)
              .Where(u => !u.IsDeleted && u.UserType == EUserType.Seat)
              .WhereIF(dto.StartTime.HasValue, (u, c) => c.CreatedTime >= dto.StartTime.Value)
              .WhereIF(dto.EndTime.HasValue, (u, c) => c.CreatedTime <= dto.EndTime.Value)
              .GroupBy((u, c) => new { c.UserName, c.UserId })
              .Select((u, c) => new BiSeatCallsDto
              {
                  Name = c.UserName,
                  UserId = c.UserId,
                  InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In, 1, 0)),
                  OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.Out, 1, 0)),
                  InAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null, 1, 0)),
                  OutAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.Out && c.AnsweredTime != null, 1, 0)),
                  InHangupImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime == null && c.RingTimes < noConnectByeTimes, 1, 0)),
                  InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime == null, 1, 0)),
                  InDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null, c.Duration, 0))),
                  OutDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.CallDirection == ECallDirection.Out && c.AnsweredTime != null, c.Duration, 0))),
                  InAvailableAnswer = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null && c.Duration >= effectiveTimes, 1, 0)),
                  InHangupImmediateWhenAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null && c.Duration < connectByeTimes, 1, 0)),
              })
              .MergeTable()
              .ToListAsync(HttpContext.RequestAborted);


        list.ForEach(d =>
        {
            d.LoginDuration = _workRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.WorkingDuration);
            if (d.LoginDuration != null)
            {
                d.LoginDuration = Math.Round(d.LoginDuration.Value, digits: 2);
            }
            d.RestDuration = _telRestRepository.Queryable().Where(q => q.UserId == d.UserId && q.CreationTime >= dto.StartTime && q.CreationTime <= dto.EndTime).Sum(q => q.RestDuration);
            d.RestDuration = Math.Round(d.RestDuration, digits: 2);
        });


        return list;
    }

    /// <summary>
    /// 小休统计
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("rests")]
    [AllowAnonymous]
    public async Task<IReadOnlyList<BiSeatRestDto>> QuerySeatRest([FromQuery] QuerySeatRestRequest dto)
    {
        if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
        dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);

        return await _telRestRepository.Queryable()
            .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName))
            .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), x => x.StaffNo.Contains(dto.StaffNo))
            .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime.Value)
            .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime.Value)
            .GroupBy(x => new { x.UserId, x.StaffNo, x.UserName })
            .Select(x => new BiSeatRestDto
            {
                UserId = x.UserId,
                StaffNo = x.StaffNo,
                UserName = x.UserName,
                RestCount = SqlFunc.AggregateCount(x.Id),
                RestDuration = SqlFunc.AggregateSum(x.RestDuration / 60) / SqlFunc.AggregateCount(x.Id)
            })
            .OrderByIF(dto.SortRule is 0, a => a.RestDuration, OrderByType.Asc)
            .OrderByIF(dto.SortRule is 1, a => a.RestDuration, OrderByType.Desc)
            .MergeTable()
            .ToListAsync(HttpContext.RequestAborted);
    }

    /// <summary>
    /// 坐席转接统计
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("seatswitch")]
    [AllowAnonymous]
    public async Task<PagedDto<BiSeatSwitchDto>> QuerySeatSwitch([FromQuery] QuerySeatSwitchRequest dto)
    {
        if (!dto.StartTime.HasValue || !dto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");

        dto.EndTime = dto.EndTime.Value.AddDays(1).AddSeconds(-1);

        var (total, items) = await _trCallRecordRepository.Queryable()
            .Where(x => !string.IsNullOrEmpty(x.AgentTransferNumber))
            .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName))
            .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.CDPN.Contains(dto.CDPN))
            .WhereIF(dto.StartTime.HasValue, x => x.CreatedTime >= dto.StartTime.Value)
            .WhereIF(dto.EndTime.HasValue, x => x.CreatedTime <= dto.EndTime.Value)
            .Select(x => new BiSeatSwitchDto
            {
                UserId = x.UserId,
                CPN = x.CPN,
                CDPN = x.CDPN,
                CreatedTime = x.CreatedTime,
                TelNo = x.AgentTransferNumber,
                UserName = x.UserName,
            })
            .OrderByDescending(x => x.CreatedTime)
            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);

        return new PagedDto<BiSeatSwitchDto>(total, _mapper.Map<IReadOnlyList<BiSeatSwitchDto>>(items));
    }

    /// <summary>
    /// 小时统计
    /// </summary>
    /// <returns></returns>
    [HttpGet("hourcall")]
    [AllowAnonymous]
    public async Task<List<TrCallHourDto>> QueryHourCall([FromQuery] DateTime StartTime, DateTime? EndTime, string source)
    {
        //获取配置
        int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
        int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
        int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
        var list = await _trCallRecordRepositoryEx.GetCallHourList(StartTime, EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, source);
        return list;
    }

    /// <summary>
    /// 小时统计--导出
    /// </summary>
    /// <returns></returns>
    [HttpPost("hourcall/export")]
    [AllowAnonymous]
    public async Task<FileStreamResult> ExportQueryHourCall([FromBody] ExportExcelDto<BiQueryHourCallDto> dto)
    {
        //获取配置
        int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
        int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
        int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
        var list = await _trCallRecordRepositoryEx.GetCallHourList(dto.QueryDto.StartTime, dto.QueryDto.EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, dto.QueryDto.Source);

        if (list != null && list.Count > 0)
        {
            list.Add(new TrCallHourDto()
            {
                HourTo = "合计",
                EffectiveCount = list.Sum(p => p.EffectiveCount),
                ConnectByeCount = list.Sum(p => p.ConnectByeCount),
                NoConnectByeCount = list.Sum(p => p.NoConnectByeCount),
                QueueByeCount = list.Sum(m => m.QueueByeCount),
                IvrByeCount = list.Sum(m => m.IvrByeCount)
            });
        }

        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);

        var dtos = list
            .Select(stu => _mapper.Map(stu, typeof(TrCallHourDto), dynamicClass))
            .Cast<object>()
            .ToList();

        var stream = ExcelHelper.CreateStream(dtos);

        return ExcelStreamResult(stream, "通话时段分析");
    }


    /// <summary>
    /// 通话时段统计明细
    /// </summary>
    /// <returns></returns>
    [HttpGet("hourcall_list")]
    public async Task<object> QueryCallList([FromQuery] DateTime StartTime, DateTime? EndTime, string type, string source, TimeSpan? startHourTo, int pageIndex, int pageSize)
    {
        //获取配置
        int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
        int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
        int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
        var list = await _trCallRecordRepositoryEx.GetCallList(StartTime, EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, type, source, startHourTo, pageIndex, pageSize);
        return list;
    }

    /// <summary>
    /// 通话时段统计明细获取基本信息
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("hourcall_list_base")]
    public async Task<object> ReTransactBaseData()
    {
        var rsp = new
        {
            CallForwardingSource = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.CallForwardingSource),
            CallForwardingType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.CallForwardingType),
        };
        return rsp;
    }

    /// <summary>
    /// 热线号码统计
    /// </summary>
    /// <param name="StartTime"></param>
    /// <param name="EndTime"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet("gateway-query")]
    public async Task<List<CallHotLineDto>> QueryGateWay(DateTime StartTime, DateTime EndTime, string gateway)
    {
        //获取配置
        int noConnectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NoConnectByeTimes)?.SettingValue[0]);
        int effectiveTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
        int connectByeTimes = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.ConnectByeTimes)?.SettingValue[0]);
        int ringTims = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.RingTims)?.SettingValue[0]);
        var list = await _trCallRecordRepositoryEx.GetCallHotLineList(StartTime, EndTime, gateway, noConnectByeTimes, effectiveTimes, connectByeTimes, ringTims);
        return list;
    }
}
