using Hotline.Caching.Interfaces;
using Hotline.CallCenter.BlackLists;
using Hotline.CallCenter.Calls;
using Hotline.CallCenter.Tels;
using Hotline.Orders;
using Hotline.Repository.SqlSugar.Extensions;
using Hotline.Share.Dtos;
using Hotline.Share.Dtos.CallCenter;
using Hotline.Share.Dtos.TrCallCenter;
using Hotline.Share.Enums.CallCenter;
using Hotline.Users;
using MapsterMapper;
using Microsoft.Extensions.Logging;
using XF.Domain.Authentications;
using XF.Domain.Cache;
using XF.Domain.Exceptions;
using XF.Domain.Repository;

namespace Hotline.Application.CallCenter;

public abstract class DefaultCallApplication : ICallApplication
{
    private readonly IRepository<Tel> _telRepository;
    private readonly IRepository<TelGroup> _telGroupRepository;
    private readonly IWorkRepository _workRepository;
    private readonly ITelRestRepository _telRestRepository;
    private readonly IRepository<CallNative> _callNativeRepository;
    private readonly IRepository<TelOperation> _telOperationRepository;
    private readonly IRepository<CallidRelation> _callIdRelationRepository;
    private readonly ITypedCache<Work> _cacheWork;
    private readonly IUserCacheManager _userCacheManager;
    private readonly ISessionContext _sessionContext;
    private readonly IMapper _mapper;
    private readonly ILogger<DefaultCallApplication> _logger;

    public DefaultCallApplication(
        IRepository<Tel> telRepository,
        IRepository<TelGroup> telGroupRepository,
        IWorkRepository workRepository,
        ITelRestRepository telRestRepository,
        IRepository<CallNative> callNativeRepository,
        IRepository<TelOperation> telOperationRepository,
        IRepository<CallidRelation> callIdRelationRepository,
        ITypedCache<Work> cacheWork,
        IUserCacheManager userCacheManager,
        ISessionContext sessionContext,
        IMapper mapper,
        ILogger<DefaultCallApplication> logger)
    {
        _telRepository = telRepository;
        _telGroupRepository = telGroupRepository;
        _workRepository = workRepository;
        _telRestRepository = telRestRepository;
        _callNativeRepository = callNativeRepository;
        _telOperationRepository = telOperationRepository;
        _callIdRelationRepository = callIdRelationRepository;
        _cacheWork = cacheWork;
        _userCacheManager = userCacheManager;
        _sessionContext = sessionContext;
        _mapper = mapper;
        _logger = logger;
    }

    public DefaultCallApplication()
    {
    }

    /// <summary>
    /// 查询分机
    /// </summary>
    public virtual async Task<IReadOnlyList<TelDto>> QueryTelsAsync(CancellationToken cancellationToken)
    {
        return await _telRepository.Queryable()
            .Select<TelDto>()
            .ToListAsync(cancellationToken);
    }

    /// <summary>
    /// 查询分机组
    /// </summary>
    public virtual async Task<IReadOnlyList<TelGroupDto>> QueryTelGroupsAsync(CancellationToken cancellationToken)
    {
        return await _telGroupRepository.Queryable()
            .Select<TelGroupDto>()
            .ToListAsync(cancellationToken);
    }

    /// <summary>
    /// 新增黑名单
    /// </summary>
    public abstract Task<string> AddBlackListAsync(AddBlacklistDto dto, CancellationToken cancellationToken);

    /// <summary>
    /// 删除黑名单
    /// </summary>
    public abstract Task RemoveBlackListAsync(string id, CancellationToken cancellationToken);

    /// <summary>
    /// 查询黑名单
    /// </summary>
    public abstract Task<List<Blacklist>> QueryBlackListsAsync(CancellationToken cancellationToken);

    /// <summary>
    /// 签入
    /// </summary>
    public virtual async Task<TrOnDutyResponseDto> SignInAsync(SignInDto dto, CancellationToken cancellationToken)
    {
        if (string.IsNullOrEmpty(dto.TelNo))
            throw UserFriendlyException.SameMessage("无效分机号");
        var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
        if (work is not null)
        {
            //if (work.TelNo != dto.TelNo)
            //{
            //    throw UserFriendlyException.SameMessage("当前用户已签入其他分机");
            //}
            throw UserFriendlyException.SameMessage("当前用户已签入");
        }

        var telWork = _userCacheManager.GetWorkByTelNoExp(dto.TelNo);
        if (telWork is not null)
        {
            throw UserFriendlyException.SameMessage("当前分机已被占用");
        }

        work = new Work(_sessionContext.RequiredUserId, _sessionContext.UserName,
            dto.TelNo, dto.TelNo, null, null,
            dto.GroupId, _sessionContext.StaffNo, null);
        await _workRepository.AddAsync(work, cancellationToken);

        return new TrOnDutyResponseDto
        {
            TelNo = dto.TelNo,
            QueueId = dto.GroupId,
            StartTime = work.StartTime,
        };
    }

    /// <summary>
    /// 签出
    /// </summary>
    public virtual async Task SingOutAsync(CancellationToken cancellationToken)
    {
        var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
        if (work is null) return;

        var telRest =
            await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
        if (telRest is not null)
        {
            telRest.EndRest();
            await _telRestRepository.UpdateAsync(telRest, cancellationToken);
        }

        work.OffDuty();
        await _workRepository.UpdateAsync(work, cancellationToken);
        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
    }

    /// <summary>
    /// 签出
    /// </summary>
    public virtual async Task SingOutAsync(string telNo, CancellationToken cancellationToken)
    {
        var work = _userCacheManager.GetWorkByTelNoExp(telNo);
        if (work is null) return;

        var telRest =
            await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
        if (telRest is not null)
        {
            telRest.EndRest();
            await _telRestRepository.UpdateAsync(telRest, cancellationToken);
        }

        work.OffDuty();
        await _workRepository.UpdateAsync(work, cancellationToken);
        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
        await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
    }

    /// <summary>
    /// 查询当前用户的分机状态
    /// </summary>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public virtual async Task<TrOnDutyResponseDto> GetTelStateAsync(CancellationToken cancellationToken)
    {
        var work = _userCacheManager.GetWorkByUserNoExp(_sessionContext.RequiredUserId);
        if (work is null) return null;
        return await Task.FromResult(new TrOnDutyResponseDto
        {
            TelNo = work.TelNo,
            QueueId = work.QueueId,
            StartTime = work.StartTime,
            StaffNo = work.StaffNo
        });
    }

    /// <summary>
    /// 定量查询通话记录
    /// </summary>
    public virtual async Task<IReadOnlyList<CallNativeDto>> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
    {
        return await _callNativeRepository.Queryable(includeDeleted: true)
            .LeftJoin<Order>((d, o) => d.Id == o.CallId)
            .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (d, o) => o.No == dto.OrderNo)
            .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo == dto.FromNo)
            .WhereIF(!string.IsNullOrEmpty(dto.ToNo), d => d.ToNo == dto.ToNo)
            .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
            .WhereIF(!string.IsNullOrEmpty(dto.TelNo), d => d.TelNo == dto.TelNo)
            .WhereIF(dto.EndBy != null, d => d.EndBy == dto.EndBy)
            .WhereIF(dto.CallStartTimeStart != null, d => d.BeginIvrTime >= dto.CallStartTimeStart)
            .WhereIF(dto.CallStartTimeEnd != null, d => d.BeginIvrTime <= dto.CallStartTimeEnd)
            .WhereIF(dto.IsConnected != null, d => d.AnsweredTime != null)
            .WhereIF(dto.Direction != null, d => d.Direction == dto.Direction)
            .WhereIF(dto.WaitDurationStart != null && dto.WaitDurationStart > 0, d => d.WaitDuration >= dto.WaitDurationStart)
            .WhereIF(dto.WaitDurationEnd != null && dto.WaitDurationEnd > 0, d => d.WaitDuration <= dto.WaitDurationEnd)
            .OrderByDescending(d => d.Id)
            .Select((d, o) => new CallNativeDto
            {
                OrderId = o.Id,
                OrderNo = o.No,
                Title = o.Title,
            }, true)
            .ToFixedListAsync(dto, cancellationToken);
    }

    /// <summary>
    /// 查询分机操作记录（定量）
    /// </summary>
    public virtual async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync(QueryTelOperationsFixedDto dto, CancellationToken cancellationToken)
    {
        return await _telOperationRepository.Queryable()
            .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
            .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo)
            .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId)
            .WhereIF(dto.OperateState != null, d => d.OperateState == dto.OperateState)
            .OrderByDescending(d => d.Id)
            .ToFixedListAsync(dto, cancellationToken);
    }

    /// <summary>
    /// 依据通话记录编号获取映射后的callId
    /// </summary>
    public virtual async Task<string> GetOrSetCallIdAsync(string callNo, CancellationToken cancellationToken)
    {
        var callOrder = await _callIdRelationRepository.GetAsync(callNo, cancellationToken);
        if (callOrder == null)
        {
            callOrder = new CallidRelation
            {
                Id = callNo,
                CallId = Ulid.NewUlid().ToString(),
            };
            try
            {
                await _callIdRelationRepository.AddAsync(callOrder, cancellationToken);
            }
            catch (Exception e)
            {
                _logger.LogError($"写入callidRelation失败：{e.Message}");
                return await GetOrSetCallIdAsync(callNo, cancellationToken);
            }
        }

        return callOrder.CallId;
    }

    public async Task<CallidRelation> GetRelationAsync(string callNo, CancellationToken cancellation)
    {
        return await _callIdRelationRepository.GetAsync(callNo, cancellation);
    }

    public async Task AddRelationAsync(CallidRelation relation, CancellationToken cancellation)
    {
        await _callIdRelationRepository.AddAsync(relation, cancellation);
    }

    /// <summary>
    /// 乐观并发更新映射关系
    /// </summary>
    public virtual async Task<int> UpdateRelationOptLockAsync(CallidRelation relation, CancellationToken cancellationToken)
    {
        return await _callIdRelationRepository.Updateable(relation).ExecuteCommandWithOptLockAsync();
    }

    /// <summary>
    /// 查询通话记录
    /// </summary>
    public virtual async Task<CallNative?> GetCallAsync(string callId, CancellationToken cancellationToken)
    {
        if (string.IsNullOrEmpty(callId)) return null;
        return await _callNativeRepository.GetAsync(callId, cancellationToken);
    }

    /// <summary>
    /// 查询通话记录
    /// </summary>
    public virtual async Task<List<CallNative>> QueryCallsAsync(
        string? phone,
        ECallDirection? direction,
        DateTime? callStartTimeStart,
        DateTime? callStartTimeEnd,
        CancellationToken cancellationToken)
    {
        if (string.IsNullOrEmpty(phone))
            return new List<CallNative>();
        return await _callNativeRepository.Queryable()
            .WhereIF(direction.HasValue, d => d.Direction == direction)
            .WhereIF(callStartTimeStart != null, d => d.BeginIvrTime >= callStartTimeStart)
            .WhereIF(callStartTimeEnd != null, d => d.BeginIvrTime <= callStartTimeEnd)
            .Where(d => d.FromNo == phone || d.ToNo == phone)
            .OrderBy(d => d.CreationTime)
            .ToListAsync(cancellationToken);
    }

    #region tianrun 临时方案

    public virtual Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// 根据转写ID获取通话信息
    /// </summary>
    /// <returns></returns>
    public virtual async Task<TrCallRecord?> GetTianrunCallTransliterationAsync(string transliterationId, CancellationToken cancellationToken)
    {
		throw new NotImplementedException();
	}

	public virtual async Task EditTransliterationStateAsync(string callId, ECallTransliterationState state, string transliterationId, CancellationToken cancellationToken)
    {
		throw new NotImplementedException();
	}

	/// <summary>
	/// 关联通话记录与order（添润）
	/// </summary>
	public virtual Task RelateTianrunCallWithOrderAsync(string callId, string orderId,
        CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// 查询通话记录
    /// </summary>
    public virtual Task<List<TrCallRecord>> QueryTianrunCallsAsync(
        string? phone = null,
        ECallDirection? direction = null,
        DateTime? callStartTimeStart = null,
        DateTime? callStartTimeEnd = null,
        CancellationToken cancellationToken = default)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// 查询分机操作选项
    /// </summary>
    /// <returns></returns>
    public abstract List<Kv> GetTelOperationOptions();

    #endregion
}