using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Spreadsheet;
using DotNetCore.CAP;
using Hotline.Api.Filter;
using Hotline.Application.CallCenter;
using Hotline.Application.Systems;
using Hotline.Application.Tels;
using Hotline.Caching.Interfaces;
using Hotline.CallCenter.Calls;
using Hotline.CallCenter.Tels;
using Hotline.CallCenter.Tels.CallTelDomain;
using Hotline.Configurations;
using Hotline.EventBus;
using Hotline.Orders;
using Hotline.Quality;
using Hotline.Quality.Notifications;
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.Quality;
using Hotline.Share.Dtos.TrCallCenter;
using Hotline.Share.Enums.CallCenter;
using Hotline.Share.Enums.Quality;
using Hotline.Share.Requests;
using Hotline.Share.Tools;
using Hotline.Tools;
using Hotline.Users;
using Mapster;
using MapsterMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using SqlSugar;
using XF.Domain.Authentications;
using XF.Domain.Cache;
using XF.Domain.Exceptions;
using XF.Domain.Filters;
using XF.Domain.Repository;
using XF.Utility.EnumExtensions;

namespace Hotline.Api.Controllers
{
    public class IPPbxController : BaseController
    {
        private readonly IMapper _mapper;
        private readonly IUserDomainService _userDomainService;
        private readonly ISessionContext _sessionContext;
        private readonly IRepository<TrCallRecord> _trCallRecordRepository;
        private readonly ICallApplication _callApplication;
        private readonly IRepository<TrCallEvaluate> _trCallEvaluate;
        private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
        private readonly ILogger<IPPbxController> _logger;
        private readonly IOrderRepository _orderRepository;
        private readonly IRepository<OrderVisit> _orderVisitRepository;
        private readonly IUserCacheManager _userCacheManager;
        private readonly ICapPublisher _capPublisher;
        private readonly ITelRestRepository _telRestRepository;
        private readonly IRepository<User> _userRepository;
        private readonly ITelApplication _telApplication;
        private readonly IRepository<Quality.Quality> _qualiteyRepository;
        private readonly IRepository<QualityTemplate> _qualityTemplate;
        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
        private readonly IRepository<TelActionRecord> _telActionRecordRepository;
        private readonly ITypedCache<Work> _cacheWork;
        private readonly ISystemMobilAreaApplication _systemMobilAreaApplication;
        private readonly IRepository<Work> _workRepository;
        private readonly Publisher _publisher;
        private readonly ITrCallRecordRepository _callRecordRepository;
        private readonly IIPPbxApplication _iPPbxApplication;
        private readonly ICallTelClient _callTelClient;
        private readonly IRepository<TelOperation> _telOperationRepository;
        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
        private readonly IRepository<VoiceEvaluationButton> _voiceEvaluationButtonRepository;
        private readonly IOrderVisitDomainService _orderVisitDomainService;

        public IPPbxController(IMapper mapper, IUserDomainService userDomainService,
            ISessionContext sessionContext, IRepository<TrCallRecord> trCallRecordRepository,
            ICallApplication callApplication, IRepository<TrCallEvaluate> trCallRecord,
            ISystemDicDataCacheManager systemDicDataCacheManager, ILogger<IPPbxController> logger,
            IOrderRepository orderRepository, IRepository<OrderVisit> orderVisitRepository,
            IUserCacheManager userCacheManager, ICapPublisher capPublisher,
            ITelRestRepository telRestRepository, IRepository<User> userRepository,
            ITelApplication telApplication, IRepository<Quality.Quality> qualiteyRepository,
            IRepository<QualityTemplate> qualityTemplate,
            ISystemSettingCacheManager systemSettingCacheManager, IRepository<TelActionRecord> telActionRecordRepository,
            ISystemMobilAreaApplication systemMobilAreaApplication,
            IRepository<Work> workRepository,
            Publisher publisher,
            ITrCallRecordRepository callRecordRepository,
            ITypedCache<Work> cacheWork,
            IIPPbxApplication iPPbxApplication,
            ICallTelClient callTelClient,
            IRepository<TelOperation> telOperationRepository,
           IOptionsSnapshot<AppConfiguration> appOptions,
           IRepository<VoiceEvaluationButton> voiceEvaluationButtonRepository,
           IOrderVisitDomainService orderVisitDomainService)
        {
            _mapper = mapper;
            _userDomainService = userDomainService;
            _sessionContext = sessionContext;
            _trCallRecordRepository = trCallRecordRepository;
            _callApplication = callApplication;
            _trCallEvaluate = trCallRecord;
            _systemDicDataCacheManager = systemDicDataCacheManager;
            _logger = logger;
            _orderRepository = orderRepository;
            _orderVisitRepository = orderVisitRepository;
            _userCacheManager = userCacheManager;
            _capPublisher = capPublisher;
            _telRestRepository = telRestRepository;
            _userRepository = userRepository;
            _telApplication = telApplication;
            _qualiteyRepository = qualiteyRepository;
            _qualityTemplate = qualityTemplate;
            _systemSettingCacheManager = systemSettingCacheManager;
            _telActionRecordRepository = telActionRecordRepository;
            _systemMobilAreaApplication = systemMobilAreaApplication;
            _workRepository = workRepository;
            _publisher = publisher;
            _callRecordRepository = callRecordRepository;
            _cacheWork = cacheWork;
            _iPPbxApplication = iPPbxApplication;
            _callTelClient = callTelClient;
            _telOperationRepository = telOperationRepository;
            _appOptions = appOptions;
            _voiceEvaluationButtonRepository = voiceEvaluationButtonRepository;
            _orderVisitDomainService = orderVisitDomainService;
        }

        #region 添添呼

        #region 分机
        /// <summary>
        /// 查询所有分机
        /// </summary>
        /// <returns></returns>
        [HttpGet("query-tels")]
        public async Task<List<TrTelDto>> TrQueryTels()
        {
            var tels = await _callTelClient.QueryTelsAsync(new QueryTelRequest() { }, HttpContext.RequestAborted);
            var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
            tels = tels.Where(m => listenTels.Contains(m.TelNo) == false).ToList();
            var returnlist = _mapper.Map<List<TrTelDto>>(tels);
            string callOutQueueId = _systemSettingCacheManager.GetSetting(SettingConstants.CallOutQueueId).SettingValue[0];
            returnlist.ForEach(x =>
            {
                x.CallOutQueue = callOutQueueId;
            });

            return returnlist;
        }

        /// <summary>
        /// 查询所有分机状态
        /// </summary>
        /// <returns></returns>
        [HttpGet("query-telstate")]
        [AllowAnonymous]
        public async Task<IReadOnlyList<TrTelStateDto>> TrQueryTelState([FromQuery] string? state, bool hasListen)
        {
            var tels = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest() { State = state }, HttpContext.RequestAborted);
            var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
            var workList = await _workRepository.Queryable().Where(d => 1 == 1 && !d.EndTime.HasValue).ToListAsync();
            var query = from tel in tels.AgentList
                        join works in workList on tel.TelNo equals works.TelNo into workD
                        from work in workD.DefaultIfEmpty()
                        select new TrTelStateDto
                        {
                            Id = tel.Id,
                            TelNo = tel.TelNo,
                            ChannelUUid = tel.ChannelUUid,
                            TelName = tel.TelName,
                            Type = tel.Type,
                            Weight = tel.Weight,
                            Queue = tel.Queue,
                            State = tel.State,
                            OldState = tel.OldState,
                            Device = tel.Device,
                            SipIp = tel.SipIp,
                            PrivateData = tel.PrivateData,
                            SipState = tel.SipState,
                            CreatedAt = tel.CreatedAt,
                            UpdatedAt = tel.UpdatedAt,
                            CallDirection = tel.CallDirection,
                            OtherNumber = tel.OtherNumber,
                            GateWay = tel.GateWay,
                            AnsweredAt = tel.AnsweredAt,
                            WorkUserId = (work != null) ? work.UserId : "",
                            WorkUserName = (work != null) ? work.UserName : "",
                        };
            //if (hasListen == false)
            //{
            //    query = query.Where(m => listenTels.Contains(m.TelNo) == false);
            //}
            var list = query.OrderBy(x => x.TelNo).OrderByDescending(x => x.CreatedAt).ToList();
            return list;// _mapper.Map<IReadOnlyList<TrTelStateDto>>(tels.AgentList);
        }

        /// <summary>
        /// 查询坐席分机状态及通话信息
        /// </summary>
        /// <param name="telno"></param>
        /// <returns></returns>
        [HttpGet("query-telstatebyno")]
        [AllowAnonymous]
        public async Task<TrTelStateDto> TrQueryTelStateByTelNo([FromQuery] string? telno)
        {
            var tels = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest() { TelNo = telno }, HttpContext.RequestAborted);

            var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
            tels.Agents = tels.Agents?.Where(m => listenTels.Contains(m.TelNo) == false).ToList();

            var tel = tels.Agents?.FirstOrDefault();
            if (tel != null)
            {
                var work = _userCacheManager.GetWorkByTel(tel.TelNo);
                var result = new TrTelStateDto
                {
                    Id = tel.Id,
                    TelNo = tel.TelNo,
                    ChannelUUid = tel.ChannelUUid,
                    TelName = tel.TelName,
                    Type = tel.Type,
                    Weight = tel.Weight,
                    Queue = tel.Queue,
                    State = tel.State,
                    OldState = tel.OldState,
                    Device = tel.Device,
                    SipIp = tel.SipIp,
                    PrivateData = tel.PrivateData,
                    SipState = tel.SipState,
                    CreatedAt = tel.CreatedAt,
                    UpdatedAt = tel.UpdatedAt,
                    CallDirection = tel.CallDirection,
                    OtherNumber = tel.OtherNumber,
                    GateWay = tel.GateWay,
                    AnsweredAt = tel.AnsweredAt,
                    WorkUserId = (work != null) ? work.UserId : "",
                    WorkUserName = (work != null) ? work.UserName : "",
                };
                result.OnStateCount = await _trCallRecordRepository.Queryable().Where(x => x.CDPN == telno && x.CallDirection == ECallDirection.In && x.OnState == EOnState.On && x.CreatedTime.Date == DateTime.Now.Date).CountAsync();
                return result;
            }
            return null;
        }




        #endregion

        #region 黑白名单

        /// <summary>
        /// 新增黑白名单
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [LogFilter("新增黑白名单")]
        [HttpPost("add-blacklist")]
        public async Task AddBlacklist([FromBody] TrAddBlacklistDto dto)
        {
            await _callTelClient.AddBlacklistAsync(new AddBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
        }

        /// <summary>
        /// 删除黑白名单
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [LogFilter("删除黑白名单")]
        [HttpPost("remove-blacklist")]
        public async Task DelBlacklist([FromBody] TrDelBlacklistDto dto)
        {
            await _callTelClient.DelBlacklistAsync(new DelBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
        }

        /// <summary>
        /// 查询黑白名单
        /// </summary>
        /// <returns></returns>
        [HttpGet("query-blacklist")]
        public async Task<IReadOnlyList<TrQueryBlacklistResponseDto>> QueryBlacklist([FromQuery] TrQueryBlacklistDto dto)
        {
            var blacklist = await _callTelClient.QueryBlacklistAsync(new QueryBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
            return _mapper.Map<IReadOnlyList<TrQueryBlacklistResponseDto>>(blacklist);
        }

        #endregion

        #region 签入后调用

        /// <summary>
        /// 上班
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost("on-duty")]
        public async Task<TrOnDutyResponseDto> OnDuty([FromBody] TrOnDutyDto dto)
        {
            return await _callApplication.SignInAsync(new SignInDto() { TelNo = dto.TelNo, TelModelState = dto.TelModelState }, HttpContext.RequestAborted);
        }

        /// <summary>
        /// 下班
        /// </summary>
        /// <returns></returns>
        [HttpPost("off-duty")]
        public async Task OffDuty()
        {
            //await _userDomainService.TrOffDutyAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
            await _telApplication.SignOutAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
        }

        /// <summary>
        /// 查询当前用户的分机状态
        /// </summary>
        /// <returns></returns>
        [HttpGet("tel-state")]
        public async Task<TrOnDutyResponseDto> TelState()
        {
            return await _callApplication.GetTelStateAsync(HttpContext.RequestAborted);
        }

        /// <summary>
        /// 切换状态
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="isCallOut"></param>
        /// <returns></returns>
        [HttpPost("change-telmodel")]
        public async Task ChangeTelModel([FromBody] ChangeTelModelDto dto)
        {
            await _callApplication.ChangeTelModel(dto.isCallOut, HttpContext.RequestAborted);
        }

        /// <summary>
        /// 下班-管理手动操作
        /// </summary>
        /// <param name="telNo"></param>
        /// <returns></returns>
        [HttpGet("off-duty-manage")]
        public async Task OffDuty([FromQuery] string telNo)
        {
            await _telApplication.SignOutByTelNoAsync(telNo, HttpContext.RequestAborted);
        }

        //提供关闭浏览器事件触发调用
        [HttpPost("off-duty-no-auth")]
        [AllowAnonymous]
        public async Task OffDutyWithNoAuthorize([FromQuery] string userId)
        {
            if (string.IsNullOrEmpty(userId))
                throw new UserFriendlyException("无效用户编号");
            await _telApplication.SignOutByTelNoAsync(userId, HttpContext.RequestAborted);
        }

        #endregion

        #region 添添呼小休
        /// <summary>
        /// 小休页面基础数据
        /// </summary>
        /// <returns></returns>
        [HttpGet("rest/basedata")]
        public async Task<object> RestBasePaegData()
        {
            var rsp = new
            {
                RestReason = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.RestReason)
            };

            return rsp;
        }

        /// <summary>
        /// 小休
        /// </summary>
        /// <returns></returns>
        [HttpPost("rest")]
        [LogFilter("分机小休")]
        public async Task Rest([FromBody] TrRestDto dto)
        {
            var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
            if (work == null)
                throw UserFriendlyException.SameMessage("分机未签入，不能休息");

            var isResting = await _telRestRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
            if (isResting)
                throw UserFriendlyException.SameMessage("当前坐席正在休息");

            var user = await _userRepository.GetAsync(work.UserId, HttpContext.RequestAborted);
            var telRest = new TelRest(work.TelNo, work.TelNo, work.UserId, work.UserName, dto.Reason, false, user.StaffNo);
            await _telRestRepository.AddAsync(telRest, HttpContext.RequestAborted);
            var telAction = new TelActionRecord(work.UserId, work.UserName, work.TelNo, work.QueueId, EActionType.TelRest);
            await _telActionRecordRepository.AddAsync(telAction, HttpContext.RequestAborted);
        }

        /// <summary>
        /// 取消小休
        /// </summary>
        /// <returns></returns>
        [HttpPost("unrest")]
        [LogFilter("分机取消小休")]
        public async Task UnRest()
        {
            var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
            if (work is null)
                throw UserFriendlyException.SameMessage("分机未签入，不能操作");

            var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, HttpContext.RequestAborted);
            if (telRest is null)
                throw UserFriendlyException.SameMessage("未查询到分机休息信息");
            telRest.EndRest();
            await _telRestRepository.UpdateAsync(telRest, HttpContext.RequestAborted);

            var telAction = await _telActionRecordRepository.GetAsync(x => x.TelNo == work.TelNo && x.ActionType == EActionType.TelRest && !x.EndTime.HasValue, HttpContext.RequestAborted);
            if (telAction != null)
            {
                telAction.EndAction();
                await _telActionRecordRepository.UpdateAsync(telAction);
            }
        }

        #endregion

        #region 添添呼话后整理

        /// <summary>
        /// 分机动作开始
        /// </summary>
        /// <returns></returns>
        [HttpGet("callennd-arrange-begin")]
        public async Task CallEndArrangeBegin(int actionType)
        {
            var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);

            if (work is null)
                throw UserFriendlyException.SameMessage("分机未签入，不能操作");

            //判断如果已经存在未结束的该动作就不新增
            var ishas = await _telActionRecordRepository.AnyAsync(x => x.TelNo == work.TelNo && x.ActionType == (EActionType)actionType && !x.EndTime.HasValue, HttpContext.RequestAborted);
            if (!ishas)
            {
                var telAction = new TelActionRecord(work.UserId, work.UserName, work.TelNo, work.QueueId, (EActionType)actionType);
                await _telActionRecordRepository.AddAsync(telAction, HttpContext.RequestAborted);
            }
        }

        /// <summary>
        /// 分机动作结束
        /// </summary>
        /// <returns></returns>
        [HttpGet("callend-arrange-end")]
        public async Task CallEndArrangeEnd(int actionType)
        {
            var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
            if (work is null)
                throw UserFriendlyException.SameMessage("分机未签入，不能操作");

            var telAction = await _telActionRecordRepository.GetAsync(x => x.TelNo == work.TelNo && x.ActionType == (EActionType)actionType && !x.EndTime.HasValue, HttpContext.RequestAborted);
            if (telAction != null)
            {
                telAction.EndAction();
                await _telActionRecordRepository.UpdateAsync(telAction);
            }
        }


        #endregion

        #region 通话记录（对外）
        /// <summary>
        /// 接收通话记录
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("receivecallrecord")]
        public async Task<OpenResponse> ReceiveCallRecord([FromBody] ReceiveCallRecordDto dto)
        {
            _logger.LogInformation($"收到通话记录结果回传：{JsonConvert.SerializeObject(dto)}");
            //bool isHas = await _trCallRecordRepository.AnyAsync(x => x.OtherAccept == dto.other_accept, HttpContext.RequestAborted);
            //if (isHas)
            //{
            //    return OpenResponse.Ok("success");
            //}

            var model = _mapper.Map<TrCallRecord>(dto);
            model.Duration = 0;
            model.RingTimes = 0;
            model.QueueTims = 0;
            model.OnState = Share.Enums.CallCenter.EOnState.NoOn;

            //计算通话时长
            if (model.AnsweredTime != null)
            {
                model.OnState = Share.Enums.CallCenter.EOnState.On; //是否接通

                TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
                TimeSpan tsbegin = new TimeSpan(model.AnsweredTime.Value.Ticks);
                model.Duration = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
            }
            //计算振铃时长
            if (model.BeginRingTime != null)
            {
                TimeSpan tsbegin = new TimeSpan(model.BeginRingTime.Value.Ticks);
                if (model.EndRingTimg != null)
                {
                    TimeSpan tsend = new TimeSpan(model.EndRingTimg.Value.Ticks);
                    model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
                }
                else
                {
                    TimeSpan tsend = new TimeSpan(model.OverTime.Ticks);
                    model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
                }
            }
            //计算队列时长
            if (model.BeginQueueTime != null && model.EndQueueTime != null)
            {
                TimeSpan tsend = new TimeSpan(model.EndQueueTime.Value.Ticks);
                TimeSpan tsbegin = new TimeSpan(model.BeginQueueTime.Value.Ticks);
                model.QueueTims = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
            }
            //获取分机号
            if (model.CallDirection == ECallDirection.In)
            {
                model.TelNo = model.CDPN;
                try
                {
                    var areaModel = await _systemMobilAreaApplication.GetPhoneCardArea(model.CPN, HttpContext.RequestAborted);
                    if (areaModel != null)
                    {
                        model.MobileAreaName = areaModel.MobileAreaName;
                        model.OFlag = areaModel.OFlag;
                        model.OperatorName = areaModel.OperatorName;
                    }
                }
                catch { }
            }
            else
            {
                model.TelNo = model.CPN;
                try
                {
                    var areaModel = await _systemMobilAreaApplication.GetPhoneCardArea(model.CDPN, HttpContext.RequestAborted);
                    if (areaModel != null)
                    {
                        model.MobileAreaName = areaModel.MobileAreaName;
                        model.OFlag = areaModel.OFlag;
                        model.OperatorName = areaModel.OperatorName;
                    }
                }
                catch { }
            }
            //判断是否是内部通话（目前分机都为4位）
            if (model.CPN.Length == 4 && model.CDPN.Length == 4) //是内部通话
            {
                model.TelNo = model.CDPN;//如果是内部通话  响应分机为被叫号码
            }
            if (!string.IsNullOrEmpty(dto.phoneTypes))
            {
                model.PhoneTypes = (EPhoneTypes)Convert.ToInt32(dto.phoneTypes);
            }


            //获取关联 工单或是回访
            //var order = await _orderRepository.GetAsync(x => x.CallId == model.CallAccept, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
            var order = await _orderRepository.GetAsync(x => x.CallId == model.OtherAccept && string.IsNullOrEmpty(x.CallId) == false, HttpContext.RequestAborted);
            if (order != null)
            {
                model.CallOrderType = ECallOrderType.Order;
                model.ExternalId = order.Id;
                try
                {
                    // 写入智能质检
                    var teAny = await _qualityTemplate.Queryable()
                        .LeftJoin<QualityTemplateDetail>((x, d) => x.Id == d.TemplateId)
                        .LeftJoin<QualityItem>((x, d, i) => d.ItemId == i.Id)
                        .Where((x, d, i) => i.IsIntelligent == 1)
                        .Where((x, d, i) => x.Grouping == ETemplateGrouping.Accepted).AnyAsync();
                    if (teAny)
                    {
                        var quality = await _qualiteyRepository.Queryable().Where(x => x.OrderId == order.Id && x.Source == Share.Enums.Quality.EQualitySource.Accepted).FirstAsync();
                        if (quality != null)
                        {
                            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ViteRecordPrefix);
                            //await _aiQualityService.CreateAiOrderQualityTask(quality, model, order, setting?.SettingValue[0], HttpContext.RequestAborted);

                            try
                            {
                                //_aiQualityService.CreateAiOrderQualityTask(
                                //quality,
                                //model.RecordingAbsolutePath,
                                //model.CPN,
                                //model.CreatedTime,
                                //order, setting?.SettingValue[0], HttpContext.RequestAborted);
                                var handler = new AiQualityHandler()
                                {
                                    Id = quality.Id,
                                    Source = quality.Source.ToString(),
                                    AudioFile = model.RecordingAbsolutePath,
                                    FromNo = model.CPN,
                                    CallStartTime = model.CreatedTime,
                                    ViteRecordPrefix = setting?.SettingValue[0],
                                };
                                await _publisher.PublishAsync(new AiOrderQualityNotify(handler), PublishStrategy.ParallelNoWait, HttpContext.RequestAborted);
                            }
                            catch (Exception e)
                            {
                                _logger.LogError($"写入智能质检异常2！, \r\n{e.Message}");
                            }

                        }
                    }
                }
                catch { }
            }
            else
            {
                //var orderVisit = await _orderVisitRepository.GetAsync(x => x.CallId == model.CallAccept, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
                var orderVisit = await _orderVisitRepository.GetAsync(x => x.CallId == model.OtherAccept && string.IsNullOrEmpty(x.CallId) == false, HttpContext.RequestAborted);
                if (orderVisit != null)
                {
                    model.CallOrderType = ECallOrderType.Visit;
                    model.ExternalId = orderVisit.Id;
                }
            }

            //获取用户
            var work = await _userCacheManager.GetWorkByTelNoLast(model.TelNo);
            if (!string.IsNullOrEmpty(model.AgentTransferNumber))
            {
                work = await _userCacheManager.GetWorkByTelNoLast(model.AgentTransferNumber);
            }
            if (work != null)
            {
                model.UserId = work.UserId;
                model.UserName = work.UserName;
                model.StaffNo = work.StaffNo;
            }
            await _trCallRecordRepository.AddAsync(model, HttpContext.RequestAborted);


            var publishCallRecordDto = new PublishCallRecrodDto() { };
            if (order != null)
            {
                var orderDto = _mapper.Map<OrderDto>(order);
                publishCallRecordDto.Order = orderDto;
            }
            var trCallDto = _mapper.Map<TrCallDto>(model);
            publishCallRecordDto.TrCallRecordDto = trCallDto;
            if (string.IsNullOrEmpty(model.AgentTransferNumber))
            {
                //推省上
                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallBye, publishCallRecordDto);
            }

            return OpenResponse.Ok("success");
        }

        /// <summary>
        /// 接受通话语音评价
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("receivecallevaluate")]
        public async Task<OpenResponse> ReceiveCallEvaluate([FromBody] ReceiveCallEvaluateDto dto)
        {
            var model = _mapper.Map<TrCallEvaluate>(dto);
            await _trCallEvaluate.AddAsync(model, HttpContext.RequestAborted);
            return OpenResponse.Ok("success");
        }

        /// <summary>
        /// 话机状态通知
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [StaffNoSessionContextFilter()]
        [HttpPost("notify/status")]
        [LogFilterAlpha("话机状态通知")]
        public async Task NotifyStatus(NotifyInDto dto)
        {
            var userName = _sessionContext.UserName;
            if (dto.Status == 0) // 签出
            {
                //await _iPPbxApplication.ResetTelStatus(null, dto.TelNo, CancellationToken.None);
            }
            //dto.Status = await _callTelClient.GetStatusAsync(dto.Status);
            //await _callApplication.EndActionAsync(dto.Adapt<EndActionInDto>());

        }

        /// <summary>
        /// 兴唐语音评价接口
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [StaffNoSessionContextFilter()]
        [HttpPost("voiceevaluation/button")]
        [LogFilterAlpha("兴唐语音评价通知")]
        public async Task VoiceEvaluationButton(VoiceEvaluationButtonDto dto)
        {
            var data = _mapper.Map<VoiceEvaluationButton>(dto);
            await _voiceEvaluationButtonRepository.AddAsync(data, HttpContext.RequestAborted);
            try
            {
                await _orderVisitDomainService.UpdateVoiceReplyAsync(data);
            }
            catch (Exception)
            {
            }
        }

        #endregion

        #region 通话记录（对内）
        /// <summary>
        /// 获取通话记录列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpGet("calls/call-list")]
        public async Task<PagedDto<TrCallDto>> GetCallList([FromQuery] GetCallListDto dto)
        {
            var (total, items) = await _callRecordRepository.GetCallList(dto)
                .ToPagedListAsync(dto.PageIndex, dto.PageSize);

            var isCallBindOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsCallBindOrder).SettingValue[0]);

            var itemsResult = _mapper.Map<List<TrCallDto>>(items);
            itemsResult.ForEach(x =>
            {
                if (isCallBindOrder)
                {
                    if (x.Order == null && !string.IsNullOrEmpty(x.OtherAccept))
                        x.IsBindOrder = true;
                    else
                        x.IsBindOrder = false;
                }
                else
                {
                    if (string.IsNullOrEmpty(x.UserId) && x.Order == null && !string.IsNullOrEmpty(x.OtherAccept))
                        x.IsBindOrder = true;
                    else
                        x.IsBindOrder = false;
                }
            });

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

        /// <summary>
        /// 通话记录导出
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost("calls/call-list/export")]
        [LogFilterAlpha("导出日志")]
        public async Task<FileStreamResult> GetCallListExport([FromBody] ExportExcelDto<GetCallListDto> dto)
        {
            var query = _callRecordRepository.GetCallList(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<TrCallDto>>(data);

            dto.ColumnInfos.ForEach(x =>
            {
                if (x.Prop == "cpn")
                {
                    x.Prop = "CPN";
                }
                if (x.Prop == "cdpn")
                {
                    x.Prop = "CDPN";
                }
            });

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

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

            var stream = ExcelHelper.CreateStream(dtos);

            return ExcelStreamResult(stream, "通话记录");
        }

        /// <summary>
        /// 通话记录基础数据
        /// </summary>
        /// <returns></returns>
        [HttpGet("calls/basedata")]
        public async Task<object> CallBaseData()
        {
            return new
            {
                OnState = EnumExts.GetDescriptions<EOnState>(),
                CallDirection = EnumExts.GetDescriptions<ECallDirection>(),
                EndBy = EnumExts.GetDescriptions<EEndBy>(),
                PhoneTypes = EnumExts.GetDescriptions<EPhoneTypes>()
            };
        }


        #endregion

        #region 关联

        /// <summary>
        /// 可关联工单
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpGet("canlink-order")]
        public async Task<PagedDto<OrderDto>> CanLinkCallRecordOrder([FromQuery] CanLinkCallRecordOrderDto dto)
        {
            var (total, items) = await _orderRepository.Queryable()
                .Where(x => string.IsNullOrEmpty(x.CallId) && x.Source == Share.Enums.Order.ESource.Hotline)
                .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword) || x.Title.Contains(dto.Keyword))
                .OrderByDescending(x => x.CreationTime)
                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);

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

        /// <summary>
        /// 可关联回访
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpGet("canlink-ordervisit")]
        public async Task<PagedDto<OrderVisitDto>> CanLinkCallRecordOrderVisit([FromQuery] CanLinkCallRecordOrderVisitDto dto)
        {
            var (total, items) = await _orderVisitRepository.Queryable()
                .Includes(x => x.Order)
                .Where(x => string.IsNullOrEmpty(x.CallId) && x.VisitType == Share.Enums.Order.EVisitType.ArtificialVisit)
                .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword))
                .OrderByDescending(x => x.CreationTime)
                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
            return new PagedDto<OrderVisitDto>(total, _mapper.Map<IReadOnlyList<OrderVisitDto>>(items));
        }

        /// <summary>
        /// 关联工单或者回访
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost("link-callrecord")]
        public async Task LinkCallRecord([FromBody] LinkCallRecordDto dto)
        {
            //var trRecord = await _trCallRecordRepository.GetAsync(x => x.CallAccept == dto.CallId, HttpContext.RequestAborted);//由CallAccept改为OtherAccept

            if (dto.IsOrder)
            {
                var trRecords = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == dto.CallId).ToListAsync(HttpContext.RequestAborted);
                foreach (var trRecord in trRecords)
                {
                    if (trRecord.CallOrderType == ECallOrderType.Order && !string.IsNullOrEmpty(trRecord.ExternalId))
                        throw UserFriendlyException.SameMessage("通话记录已经关联工单");

                    //工单
                    var order = await _orderRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
                    //if (!string.IsNullOrEmpty(order.CallId))
                    //    throw UserFriendlyException.SameMessage("通话记录已经关联工单");

                    order.CallId = dto.CallId;
                    order.FromPhone = trRecord.CPN;
                    order.TransferPhone = trRecord.Gateway;
                    await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
                    trRecord.CallOrderType = ECallOrderType.Order;
                    trRecord.ExternalId = order.Id;
                    await _trCallRecordRepository.UpdateAsync(trRecord, HttpContext.RequestAborted);
                    //推省上
                    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallConnectWithOrder, new PublishCallRecrodDto() { Order = _mapper.Map<OrderDto>(order), TrCallRecordDto = _mapper.Map<TrCallDto>(trRecord) });
                }
            }
            else
            {
                var trRecord = await _trCallRecordRepository.GetAsync(x => x.OtherAccept == dto.CallId, HttpContext.RequestAborted);
                //回访
                var visit = await _orderVisitRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
                visit.CallId = dto.CallId;
                await _orderVisitRepository.UpdateAsync(visit, HttpContext.RequestAborted);
                trRecord.CallOrderType = ECallOrderType.Visit;
                trRecord.ExternalId = visit.Id;
                await _trCallRecordRepository.UpdateAsync(trRecord, HttpContext.RequestAborted);
            }

        }



        #endregion

        #region 坐席动作记录

        /// <summary>
        /// 坐席动作列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpGet("telaction-list")]
        public async Task<PagedDto<TelActionListRep>> TelActionList([FromQuery] TelActionListDto dto)
        {
            var (total, items) = await _telActionRecordRepository.Queryable()
                .WhereIF(string.IsNullOrEmpty(dto.TelNo) == false, x => x.TelNo.Contains(dto.TelNo))
                .WhereIF(dto.ActionTtype != null, x => x.ActionType == dto.ActionTtype)
                .WhereIF(string.IsNullOrEmpty(dto.UserName) == false, x => x.UserName.Contains(dto.UserName))
                .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
                .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
                .WhereIF(_appOptions.Value.IsLuZhou,x=>x.ActionType != EActionType.CallEndArrange)
                .OrderByDescending(x => x.CreationTime)
                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
            return new PagedDto<TelActionListRep>(total, _mapper.Map<IReadOnlyList<TelActionListRep>>(items));
        }

        /// <summary>
        /// 坐席动作列表导出
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost("telaction-list/export")]
        [LogFilterAlpha("导出日志")]
        public async Task<FileStreamResult> TelActionListExport([FromBody] ExportExcelDto<TelActionListDto> dto)
        {
            var total = 0;
            var items = new List<TelActionRecord>();

            var query = _telActionRecordRepository.Queryable()
                .WhereIF(string.IsNullOrEmpty(dto.QueryDto.TelNo) == false, x => x.TelNo.Contains(dto.QueryDto.TelNo))
                .WhereIF(dto.QueryDto.ActionTtype != null, x => x.ActionType == dto.QueryDto.ActionTtype)
                .WhereIF(string.IsNullOrEmpty(dto.QueryDto.UserName) == false, x => x.UserName.Contains(dto.QueryDto.UserName))
                .WhereIF(dto.QueryDto.StartTime.HasValue, x => x.CreationTime >= dto.QueryDto.StartTime)
                .WhereIF(dto.QueryDto.EndTime.HasValue, x => x.CreationTime <= dto.QueryDto.EndTime)
                .OrderByDescending(x => x.CreationTime);

            if (dto.IsExportAll)
            {
                items = await query.ToListAsync();
            }
            else
            {
                (total, items) = await query.ToPagedListAsync(dto.QueryDto.PageIndex, dto.QueryDto.PageSize);
            }

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

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

            var stream = ExcelHelper.CreateStream(dtos);
            return ExcelStreamResult(stream, "坐席动作列表");
        }

        /// <summary>
        /// 坐席动作列表基础数据
        /// </summary>
        /// <returns></returns>
        [HttpGet("telaction-basedata")]
        public async Task<object> TelActionBaseData()
        {
            var actionType = EnumExts.GetDescriptions<EActionType>();
            if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
            {
                actionType = actionType.Where(m => new int[] { 4, 5 }.Contains(m.Key) == false).ToList();
            }
            return new
            {
                ActionType = actionType,
            };
        }
        #endregion

        #region 话务队列信息
        /// <summary>
        /// 今日等待数
        /// </summary>
        /// <returns></returns>
        [HttpGet("query-todaywaitnum")]
        public async Task<int> QueryToDayWaitNum()
        {
            int count = await _trCallRecordRepository.Queryable()
                        .Where(x => x.CreatedTime.Date == DateTime.Now.Date)
                        .Where(x => x.QueueTims > 0 && x.Duration == 0)
                        .CountAsync();
            return count;
        }

        #endregion

        #region 话机状态

        /// <summary>
        /// 重置话机状态
        /// </summary>
        /// <returns></returns>
        [HttpPost("tels-status-refreshservice")]
        [AllowAnonymous]
        public async Task TelsStatusRefreshService()
        {
            try
            {
                var list = await _workRepository.Queryable().Where(x => 1 == 1 && !x.EndTime.HasValue).ToListAsync();
                var tellist = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest { }, HttpContext.RequestAborted);
                foreach (var item in list)
                {
                    var telmodel = tellist.AgentList.First(x => x.TelNo == item.TelNo);
                    if (telmodel != null)
                    {
                        if (telmodel.State == "logout")
                        {
                            await _iPPbxApplication.ResetTelStatus(item.Id, null, HttpContext.RequestAborted);
                            //var telRest = await _telRestRepository.GetAsync(x => x.TelNo == item.TelNo && !x.EndTime.HasValue, HttpContext.RequestAborted);
                            //if (telRest is not null)
                            //{
                            //    telRest.EndRest();
                            //    await _telRestRepository.UpdateAsync(telRest, HttpContext.RequestAborted);
                            //}

                            //item.OffDuty();
                            //await _workRepository.UpdateAsync(item, HttpContext.RequestAborted);
                            //_cacheWork.Remove(item.GetKey(KeyMode.UserId));
                            //_cacheWork.Remove(item.GetKey(KeyMode.TelNo));
                            //var listx = await _telActionRecordRepository.Queryable().Where(x => x.TelNo == item.TelNo && !x.EndTime.HasValue).ToListAsync();
                            //foreach (var itemx in listx)
                            //{
                            //    itemx.EndAction();
                            //    await _telActionRecordRepository.UpdateAsync(itemx);
                            //}
                        }
                    }
                }
            }
            catch
            {

            }
        }

        #endregion

        #endregion
    }
}
