using Hotline.Api.Filter;
using Hotline.Application.Users;
using Hotline.Caching.Interfaces;
using Hotline.CallCenter.Tels;
using Hotline.Identity.Accounts;
using Hotline.Identity.Roles;
using Hotline.Permissions;
using Hotline.Repository.SqlSugar.Extensions;
using Hotline.Settings.CommonOpinions;
using Hotline.Share.Dtos;
using Hotline.Share.Dtos.Order;
using Hotline.Share.Dtos.Users;
using Hotline.Share.Enums.CallCenter;
using Hotline.Share.Enums.Order;
using Hotline.Share.Enums.User;
using Hotline.Share.Requests;
using Hotline.Tools;
using Hotline.Users;
using MapsterMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using SqlSugar;
using System.Data;
using XF.Domain.Authentications;
using XF.Domain.Exceptions;
using XF.Domain.Options;
using XF.Domain.Repository;
using XF.Utility.EnumExtensions;

namespace Hotline.Api.Controllers;

/// <summary>
/// 用户管理相关接口
/// </summary>
public class UserController : BaseController
{
    private readonly ISessionContext _sessionContext;
    private readonly IUserDomainService _userDomainService;
    private readonly ITelRepository _telRepository;
    private readonly IRepository<User> _userRepository;
    private readonly IWorkRepository _workRepository;
    private readonly ITelCacheManager _telCacheManager;
    private readonly IUserCacheManager _userCacheManager;
    private readonly IMapper _mapper;
    private readonly IAccountRepository _accountRepository;
    private readonly IAccountDomainService _accountDomainService;
    private readonly IOptions<IdentityConfiguration> _identityConfigurationAccessor;
    private readonly ITelRestRepository _telRestRepository;
    private readonly IRepository<SystemCommonOpinion> _commonOpinionRepository;
    private readonly IUserApplication _userApplication;

    public UserController(
        ISessionContext sessionContext,
        IUserDomainService userDomainService,
        ITelRepository telRepository,
        IRepository<User> userRepository,
        IWorkRepository workRepository,
        ITelCacheManager telCacheManager,
        IUserCacheManager userCacheManager,
        IMapper mapper,
        IAccountRepository accountRepository,
        IAccountDomainService accountDomainService,
        IOptions<IdentityConfiguration> identityConfigurationAccessor,
        ITelRestRepository telRestRepository,
        IRepository<SystemCommonOpinion> commonOpinionRepository,
        IUserApplication userApplication)
    {
        _sessionContext = sessionContext;
        _userDomainService = userDomainService;
        _telRepository = telRepository;
        _userRepository = userRepository;
        _workRepository = workRepository;
        _telCacheManager = telCacheManager;
        _userCacheManager = userCacheManager;
        _mapper = mapper;
        _accountRepository = accountRepository;
        _accountDomainService = accountDomainService;
        _identityConfigurationAccessor = identityConfigurationAccessor;
        _telRestRepository = telRestRepository;
        _commonOpinionRepository = commonOpinionRepository;
        _userApplication = userApplication;
    }


    #region 小休申请

    /// <summary>
    /// 小休申请列表
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpGet("rest-apply-paged")]
    public async Task<PagedDto<RestDto>> RestApplyList([FromQuery] RestPagedDto dto)
    {
        var (total, items) = await _telRestRepository.Queryable(includeDeleted: false)
            .WhereIF(!string.IsNullOrEmpty(dto.KeyWords),
                d => d.UserName.Contains(dto.KeyWords) || d.StaffNo.Contains(dto.KeyWords))
            .WhereIF(dto.BeginTime != null && dto.BeginTime != DateTime.MinValue, d => d.CreationTime >= dto.BeginTime)
            .WhereIF(dto.EndTime != null && dto.EndTime != DateTime.MinValue, d => d.CreationTime <= dto.EndTime)
            .WhereIF(!string.IsNullOrEmpty(dto.Reason), d => d.Reason == dto.Reason)
            .WhereIF(dto.Status != null, d => d.ApplyStatus == dto.Status)
            .OrderByDescending(d => d.CreationTime)
            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);

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

    /// <summary>
    /// 小休申请页面基础信息
    /// </summary>
    /// <returns></returns>
    [HttpGet("rest-apply-basedata")]
    public object RestApplyBaseData()
    {
        return new
        {
            RestApplyStatus = EnumExts.GetDescriptions<ETelRestApplyStatus>(),
            RestReason = _commonOpinionRepository.Queryable()
                .Where(x => x.CommonType == Share.Enums.Settings.ECommonType.RestReason).ToList()
        };
    }

    #endregion

    /// <summary>
    /// 上班
    /// </summary>
    [HttpPost("on-duty")]
    public async Task OnDuty([FromBody] OnDutyDto dto)
    {
        var telNo = dto.TelNo;
        if (string.IsNullOrEmpty(telNo))
        {
            var user = await _userRepository.GetAsync(d => d.Id == _sessionContext.RequiredUserId,
                HttpContext.RequestAborted);
            if (user == null)
                throw UserFriendlyException.SameMessage("无效用户编号");
            if (string.IsNullOrEmpty(user.DefaultTelNo))
                throw UserFriendlyException.SameMessage("未设置默认分机号");
            telNo = user.DefaultTelNo;
        }

        var tel = _telCacheManager.GetTel(telNo);
        await _userDomainService.OnDutyAsync(_sessionContext.RequiredUserId, tel, HttpContext.RequestAborted);
    }

    /// <summary>
    /// 下班
    /// </summary>
    [HttpPost("off-duty")]
    public Task<WorkDto?> OffDuty()
    {
        return _userDomainService.OffDutyAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
    }

    /// <summary>
    /// 分页查询用户
    /// </summary>
    /// <returns></returns>
    [HttpGet("paged")]
    public async Task<PagedDto<UserDto>> QueryPaged([FromQuery] UserPagedDto dto)
    {
        var query =  _userApplication.QueryPaged(dto);
        Console.WriteLine(query.ToSqlString());
        var (total, items) = await query.ToPagedListAsync(dto, HttpContext.RequestAborted);

        //var (total, items) = await _userRepository.Queryable(includeDeleted: true)
        //    .Includes(d => d.Account)
        //    .Includes(d => d.Roles)
        //    .Includes(d => d.Organization)
        //    .Where(d => d.Account.AccountType == EAccountType.Personal && d.Id != SysAccountSeedData.Id)
        //    .WhereIF(_sessionContext.OrgIsCenter == false, d => d.OrgId.StartsWith(_sessionContext.RequiredOrgId))
        //    .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
        //        d => d.Name.Contains(dto.Keyword!) || d.PhoneNo.Contains(dto.Keyword!) ||
        //             d.Account.UserName.Contains(dto.Keyword))
        //    .WhereIF(!string.IsNullOrEmpty(dto.OrgCode), d => d.OrgId == dto.OrgCode)
        //    .WhereIF(!string.IsNullOrEmpty(dto.Role), d => d.Roles.Any(x => x.Id == dto.Role))
        //    .WhereIF(!string.IsNullOrEmpty(dto.Name), d => d.Name.Contains(dto.Name))
        //    .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo), d => d.PhoneNo.Contains(dto.PhoneNo))
        //    .WhereIF(!string.IsNullOrEmpty(dto.OrgName), d => d.Organization.Name.Contains(dto.OrgName))
        //    .OrderBy(d => d.Account.Status)
        //    .OrderBy(d => d.Organization.OrgType)
        //    //.OrderBy(d => d.Organization.Id)
        //    .OrderByDescending(d => d.CreationTime)
        //    .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);

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

    /// <summary>
    /// 分页查询用户---导出
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("paged/_export")]
	[LogFilterAlpha("导出日志")]
	public async Task<FileStreamResult> QueryPagedExport([FromBody] ExportExcelDto<UserPagedDto> dto)
    {
        var query =  _userApplication.QueryPaged(dto.QueryDto);
        List<User> 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<UserDto>>(data);

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

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

        var stream = ExcelHelper.CreateStream(dtos);

        return ExcelStreamResult(stream, "部门通讯录数据");
    }

    /// <summary>
    /// 更新用户
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPut]
    public async Task Update([FromBody] UpdateUserDto dto)
    {
        //工号不能重复
        if (!string.IsNullOrEmpty(dto.StaffNo))
        {
            var isStaffNoExists = await _userRepository.Queryable()
                .AnyAsync(d => d.Id != dto.Id && d.StaffNo == dto.StaffNo, HttpContext.RequestAborted);
            if (isStaffNoExists)
                throw UserFriendlyException.SameMessage("工号已存在");
        }

        var user = await _userRepository.Queryable()
            .Includes(d => d.Account)
            .FirstAsync(d => d.Id == dto.Id, HttpContext.RequestAborted);
        if (user is null)
            throw UserFriendlyException.SameMessage("无效用户编号");
        if (user.IsDeleted)
            throw UserFriendlyException.SameMessage("账号不存在");
        CheckAccountStatus(user.Account);

        _mapper.Map(dto, user);
        await _userRepository.UpdateNav(user).Include(d => d.Account)
            .ExecuteCommandAsync();

        //set roles
        await _accountRepository.SetAccountRolesAsync(user.Id, dto.RoleIds, HttpContext.RequestAborted);
    }

    /// <summary>
    /// 解锁用户
    /// </summary>
    /// <returns></returns>
    [HttpPut("unlock")]
    [LogFilter("解锁用户")]
    public async Task<string> UnlockUserAsync([FromQuery]string id)
    {
        var user = await _accountRepository.GetAsync(id) ??
            throw UserFriendlyException.SameMessage("用户不存在");
        user.LockoutEnd = null;
        await _accountRepository.UpdateNullAsync(user, HttpContext.RequestAborted);
        return "解锁成功";
    }

    /// <summary>
    /// 新增用户
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost]
    public async Task<string> Add([FromBody] AddUserDto dto)
    {
        //工号不能重复
        if (!string.IsNullOrEmpty(dto.StaffNo))
        {
            var isStaffNoExists = await _userRepository.Queryable()
                .AnyAsync(d => d.StaffNo == dto.StaffNo, HttpContext.RequestAborted);
            if (isStaffNoExists)
                throw UserFriendlyException.SameMessage("工号已存在");
        }

        var account = await _accountRepository.GetAsync(d => d.UserName == dto.UserName, HttpContext.RequestAborted);
        if (account is null)
        {
            account = _mapper.Map<Account>(dto);
            var jwtOptions = _identityConfigurationAccessor.Value.Jwt;
            if (string.IsNullOrEmpty(jwtOptions.Issuer))
                throw new UserFriendlyException("jwt.Issuer未配置");
            account.ClientId = jwtOptions.Issuer;
            account.Scope = jwtOptions.Scope;
            await _accountRepository.AddAsync(account, HttpContext.RequestAborted);
            var user = _mapper.Map<User>(dto);
            user.Id = account.Id;
            await _userRepository.AddAsync(user, HttpContext.RequestAborted);

            //initial pwd
            await _accountDomainService.InitialPasswordAsync(account, HttpContext.RequestAborted);

            //set roles
            await _accountRepository.SetAccountRolesAsync(account.Id, dto.RoleIds, HttpContext.RequestAborted);
            return account.Id;
        }
        else
        {
            //if (_accountDomainService.IsLockedOut(account))
            //    throw UserFriendlyException.SameMessage("该账号已被锁定，请联系管理员");

            ////set roles
            //await _accountRepository.SetAccountRolesAsync(account.Id, dto.RoleIds, HttpContext.RequestAborted);

            //var user = await _userRepository.GetAsync(account.Id, HttpContext.RequestAborted);
            //if (user is null)
            //{
            //    user = _mapper.Map<User>(dto);
            //    user.Id = account.Id;
            //    return await _userRepository.AddAsync(user, HttpContext.RequestAborted);
            //}

            //if (user.IsDeleted)
            //{
            //    user.Recover();
            //    _mapper.Map(dto, user);
            //    await _userRepository.UpdateAsync(user);
            //    return user.Id;
            //}

            throw UserFriendlyException.SameMessage("用户已存在");
        }
    }

    /// <summary>
    /// 删除用户
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpDelete("{id}")]
    [LogFilter("删除用户")]
    public async Task Remove(string id)
    {
        var work = await _workRepository.GetCurrentWorkByUserAsync(id, HttpContext.RequestAborted);
        if (work is not null)
            throw UserFriendlyException.SameMessage("用户正在工作中，请下班以后再删除");

        var account = await _accountRepository.GetAsync(id, HttpContext.RequestAborted);
        if (account is not null)
        {
            await _accountDomainService.UnRegisterAsync(account, HttpContext.RequestAborted);
            await _userRepository.RemoveAsync(id, true, HttpContext.RequestAborted);
        }
    }

    /// <summary>
    /// 查询用户当前状态
    /// </summary>
    /// <returns></returns>
    [HttpGet("state")]
    public async Task<UserStateDto> GetUserState()
    {
        var userId = _sessionContext.RequiredUserId;
        var isOnDuty = await _userCacheManager.IsWorkingByUserAsync(userId, HttpContext.RequestAborted);
        var isResting = false;
        var telNo = string.Empty;
        if (isOnDuty)
        {
            var work = _userCacheManager.GetWorkByUser(userId);
            isResting = await _telRestRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
            telNo = work.TelNo;
        }

        return new UserStateDto(isOnDuty, isResting, telNo);
    }

    /// <summary>
    /// 查询用户角色
    /// </summary>
    /// <returns></returns>
    [HttpGet("{id}/roles")]
    public async Task<IReadOnlyList<Role>> GetUserRoles(string id)
    {
        var account = await _accountRepository.Queryable()
            .Includes(d => d.Roles)
            .FirstAsync(d => d.Id == id);
        if (account == null)
            throw UserFriendlyException.SameMessage("无效账号编号");
        return account.Roles;
    }

    /// <summary>
    /// 设置用户角色
    /// </summary>
    /// <returns></returns>
    [HttpPost("roles")]
    public async Task SetUserRoles([FromBody] SetUserRolesDto dto)
    {
        await _accountRepository.SetAccountRolesAsync(dto.UserId, dto.RoleIds, HttpContext.RequestAborted);
    }

    /// <summary>
    /// 查询密码更改状态
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UserFriendlyException"></exception>
    [HttpGet("pwd-changed")]
    public Task<bool> GetPasswordChangeStatus()
    {
        var claim = User.Claims.FirstOrDefault(d => d.Type == AppClaimTypes.UserPasswordChanged);
        if (claim is null)
            throw UserFriendlyException.SameMessage("无密码更改信息");
        return Task.FromResult(Convert.ToBoolean(claim.Value));
    }

    /// <summary>
    /// 修改密码
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("change-pwd")]
    public async Task ChangePassword([FromBody] ChangePasswordDto dto)
    {
        var account = await _accountRepository.GetAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
        CheckAccountStatus(account);

        var result = await _accountDomainService.ResetPasswordAsync(account, dto.CurrentPassword, dto.NewPassword,
            HttpContext.RequestAborted);
        if (!result.Succeeded)
            throw UserFriendlyException.SameMessage(string.Join(',',
                result.Errors.Select(d => d.Description).ToList()));
        account.PasswordChanged = true;
        await _accountRepository.UpdateAsync(account, HttpContext.RequestAborted);
    }

    /// <summary>
    /// 修改默认密码
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    [HttpPost("change-default-pwd")]
    public async Task ChangeDefaultPassword([FromBody] NewPasswordDto dto)
    {
        var account = await _accountRepository.GetAsync(_sessionContext.RequiredUserId, HttpContext.RequestAborted);
        CheckAccountStatus(account);

        var accountOptions = _identityConfigurationAccessor.Value.Account;
        var result = await _accountDomainService.ResetPasswordAsync(account, accountOptions.DefaultPassword,
            dto.NewPassword,
            HttpContext.RequestAborted);
        if (!result.Succeeded)
            throw UserFriendlyException.SameMessage(string.Join(',',
                result.Errors.Select(d => d.Description).ToList()));
        account.PasswordChanged = true;
        await _accountRepository.UpdateAsync(account, HttpContext.RequestAborted);
    }

    /// <summary>
    /// 重置密码
    /// </summary>
    /// <returns></returns>
    [HttpPost("initial-pwd/{userId}")]
    public async Task InitialPassword(string userId)
    {
        var account = await _accountRepository.GetAsync(userId, HttpContext.RequestAborted);
        CheckAccountStatus(account);
        await _accountDomainService.InitialPasswordAsync(account, HttpContext.RequestAborted);
    }

    /// <summary>
    /// 根据id批量查询用户
    /// </summary>
    /// <param name="ids"></param>
    /// <returns></returns>
    [HttpPost("range")]
    public async Task<IReadOnlyList<UserDto>> Query([FromBody] IReadOnlyList<string> ids)
    {
        var users = await _userRepository.Queryable()
            .Includes(d => d.Account, x => x.Roles)
            .Includes(d => d.Organization)
            .Where(d => ids.Contains(d.Id))
            .OrderByDescending(d => d.CreationTime)
            .ToListAsync();
        return _mapper.Map<IReadOnlyList<UserDto>>(users);
    }

    /// <summary>
    /// 根据姓名模糊查询用户
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    [HttpGet("withorg")]
    public async Task<IReadOnlyList<UserDto>> Query([FromQuery] string name)
    {
        var users = await _userRepository.Queryable()
            .Includes(d => d.Organization)
            .Where(d => d.Name.Contains(name))
            .OrderByDescending(d => d.Name)
            .ToListAsync();
        return _mapper.Map<IReadOnlyList<UserDto>>(users);
    }

    [HttpGet("base-data")]
    public object BaseData()
    {
        return new
        {
            GenderOptions = EnumExts.GetDescriptions<EGender>(),
            UserTypeOptions = EnumExts.GetDescriptions<EUserType>(),
        };
    }


    private void CheckAccountStatus(Account? account)
    {
        if (account == null)
            throw UserFriendlyException.SameMessage("无效账号编号");
        if (_accountDomainService.IsLockedOut(account))
            throw UserFriendlyException.SameMessage("账号已被锁定");
        if (account.IsDeleted)
            throw UserFriendlyException.SameMessage("账号不存在");
    }
}