using System.Security.Claims;
using Hotline.Caching.Interfaces;
using Hotline.Identity;
using Hotline.Identity.Accounts;
using Hotline.Orders;
using Hotline.Push;
using Hotline.Schedulings;
using Hotline.Settings;
using Hotline.Share.Dtos.FlowEngine;
using Hotline.Share.Dtos.Identity;
using Hotline.Share.Enums.FlowEngine;
using Hotline.Share.Enums.Identity;
using Hotline.Users;
using IdentityModel;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using XF.Domain.Authentications;
using XF.Domain.Cache;
using XF.Domain.Constants;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Options;
using XF.Domain.Repository;

namespace Hotline.Application.Identity;

public class IdentityAppService : IIdentityAppService, IScopeDependency
{
    private readonly IAccountRepository _accountRepository;
    private readonly IAccountDomainService _accountDomainService;
    private readonly IRepository<User> _userRepository;
    private readonly IJwtSecurity _jwtSecurity;
    private readonly IOptionsSnapshot<IdentityConfiguration> _identityOptionsAccessor;
    private readonly ITypedCache<AudienceTicket> _cacheAudience;
    private readonly IMessageCodeDomainService _messageCodeDomainService;
    private readonly IRepository<Scheduling> _schedulingRepository;
    private readonly IOrderDomainService _orderDomainService;
    private readonly ISystemSettingCacheManager _systemSettingCacheManager;

	public IdentityAppService(
        IAccountRepository accountRepository,
        IAccountDomainService accountDomainService,
        IRepository<User> userRepository,
        IJwtSecurity jwtSecurity,
        IOptionsSnapshot<IdentityConfiguration> identityOptionsAccessor,
        ITypedCache<AudienceTicket> cacheAudience,
        IMessageCodeDomainService messageCodeDomainService,
        IRepository<Scheduling> schedulingRepository,
        IOrderDomainService orderDomainService,
        ISystemSettingCacheManager systemSettingCacheManager)
    {
        _accountRepository = accountRepository;
        _accountDomainService = accountDomainService;
        _userRepository = userRepository;
        _jwtSecurity = jwtSecurity;
        _identityOptionsAccessor = identityOptionsAccessor;
        _cacheAudience = cacheAudience;
        _messageCodeDomainService = messageCodeDomainService;
        _schedulingRepository = schedulingRepository;
        _orderDomainService = orderDomainService;
        _systemSettingCacheManager = systemSettingCacheManager;

	}

    public async Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken)
    {
        var account = await _accountRepository.GetExtAsync(
            d => d.UserName == dto.Username,
            d => d.Includes(x => x.Roles));
        if (account == null)
            throw new UserFriendlyException($"用户名或密码错误！{System.Text.Json.JsonSerializer.Serialize(dto)}", "用户名或密码错误！");

        var userInfo = await _userRepository.GetAsync(p => p.Id == account.Id, cancellationToken);

        //校验验证码
        await _messageCodeDomainService.CheckdCode(account.UserName, userInfo.PhoneNo, dto.MsgCode, cancellationToken);

        if (account.Status != EAccountStatus.Normal)
            throw UserFriendlyException.SameMessage("用户名或密码错误！");

        if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
            throw UserFriendlyException.SameMessage("账号被锁定！");

        var verifyResult = _accountDomainService.VerifyPassword(account, dto.Password);
        if (verifyResult == PasswordVerificationResult.Failed)
        {
            var lockoutOptions = _identityOptionsAccessor.Value.Lockout;
            account.AccessFailedCount += 1;
            if (account.LockoutEnabled && account.AccessFailedCount >= lockoutOptions.MaxFailedAccessAttempts)
                account.LockoutEnd = DateTime.Now.Add(lockoutOptions.DefaultLockoutTimeSpan);
            await _accountRepository.UpdateAsync(account, cancellationToken);
            throw new UserFriendlyException($"用户名或密码错误！{System.Text.Json.JsonSerializer.Serialize(dto)}", "用户名或密码错误！");
        }

        //限制系统类型账户频繁获取token的行为
        //todo

        if (account.LockoutEnd.HasValue || account.AccessFailedCount > 0)
        {
            account.LockoutEnd = null;
            account.AccessFailedCount = 0;
            await _accountRepository.UpdateAsync(account, cancellationToken);
        }

        var user = await _userRepository.Queryable()
            .Includes(d => d.Organization)
            .FirstAsync(d => d.Id == account.Id);
        if (user == null)
            throw UserFriendlyException.SameMessage("未查询到用户数据");
		//平均派单
		var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
		if (averageSendOrder)
		{
			await AverageOrderScheduling(account.Id, cancellationToken);
		}
        var jwtOptions = _identityOptionsAccessor.Value.Jwt;
        var claims = new List<Claim>
        {
            //new(JwtClaimTypes.Id, account.Id),
            new(JwtClaimTypes.Subject, account.Id),
            new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
            new(AppClaimTypes.UserDisplayName, account.Name),
            new(JwtClaimTypes.Scope, jwtOptions.Scope),
            new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
            new(AppClaimTypes.StaffNo, user.StaffNo ?? string.Empty),
        };
        if (!string.IsNullOrEmpty(user.OrgId) && user.Organization is not null)
        {
            claims.AddRange(
                new List<Claim>
                {
                    new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
                    new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString()),
                    new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
                    new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
                    new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
                    new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
                    new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
                }
            );
        }
        claims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
        var audience = new AudienceTicket(account.Id);
        var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
        await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds), cancellationToken);
        var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
        return token;
    }

    public async Task AverageOrderScheduling(string id, CancellationToken cancellationToken)
    {
        try
        {
            DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
            
            //&& x.AtWork!.Value != true
            //根据当前时间获取排班信息
            var scheduling = await _schedulingRepository.Queryable()
                .Includes(x => x.SchedulingUser)
                .Where(x => x.SchedulingTime == time &&
                            x.WorkingTime <= DateTime.Now.TimeOfDay && 
                            x.OffDutyTime >= DateTime.Now.TimeOfDay && 
                            (x.AtWork == true || x.AtWork == null) && 
                            x.SchedulingUser.UserId == id)
                .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
            if (scheduling != null)
            {
                scheduling.AtWork = true;
                //执行登录平均派单
                await _orderDomainService.LogAverageOrder(id, scheduling, cancellationToken);
            }
        }
        catch
        {
            // ignored
        }
    }
}