using Furion.DatabaseAccessor.Extensions;
using Furion.EventBus;
using Microsoft.Extensions.Options;
using UAParser;
using YBEE.EQM.Core;

namespace YBEE.EQM.Application;

/// <summary>
/// 认证服务
/// </summary>
public class SysAuthService(IOptions<AuthOptions> options,
    IHttpContextAccessor httpContextAccessor,
    IEventPublisher eventPublisher,
    IRepository<SysUser> userRep,
    ISysRoleUserService roleUserService,
    ISysMenuService menuService,
    IGeneralCaptchaService captchaService) : ISysAuthService, ITransient
{
    private readonly AuthOptions _authOptions = options.Value;

    /// <summary>
    /// 登录处理
    /// </summary>
    /// <param name="accountLoginInput">账户密码</param>
    /// <returns></returns>
    private async Task<AuthOutput> Login(LoginInput accountLoginInput)
    {
        var checkCaptcha = captchaService.CheckCode(accountLoginInput.Captcha);
        if (checkCaptcha.Code != 0)
        {
            throw Oops.Oh(checkCaptcha.Message);
        }

        string pwd = "";
        string newPwd = "";
        try
        {
            pwd = RSAEncryption.Decrypt(accountLoginInput.Password, _authOptions.RsaPrivateKey);
            if (!string.IsNullOrEmpty(accountLoginInput.NewPassword))
            {
                newPwd = RSAEncryption.Decrypt(accountLoginInput.NewPassword, _authOptions.RsaPrivateKey);
                if (pwd == newPwd)
                {
                    throw Oops.Oh(ErrorCode.E1009);
                }
            }
        }
        catch
        {
            throw Oops.Oh("密码格式错误！");
        }

        //// 判断用户名和密码是否正确 忽略全局过滤器
        //var users = await _userRep.DetachedEntities
        //                          .Include(t => t.SysOrg)
        //                          .Where(u => (u.Account.Equals(accountLoginInput.Account) || u.Mobile.Equals(accountLoginInput.Account) || u.Email.Equals(accountLoginInput.Account)) && u.IsDeleted == false)
        //                          .ToListAsync();
        var user = await userRep.Include(t => t.SysOrg)
                                 .FirstOrDefaultAsync(u => u.IsDeleted == false &&
                                                         (u.Account.Equals(accountLoginInput.Account) ||
                                                            u.Mobile.Equals(accountLoginInput.Account) ||
                                                            u.Email.Equals(accountLoginInput.Account)
                                                         )) ?? throw Oops.Oh(ErrorCode.E1001);
        //if (!AESEncryption.Decrypt(user.Password, _authOptions.AesPassword).Equals(pwd))
        if (!PBKDF2Encryption.Compare(pwd, user.Password))
        {
            throw Oops.Oh(ErrorCode.E1001);
        }
        //var user = (users?.FirstOrDefault(u => AESEncryption.Decrypt(u.Password, _authOptions.AesPassword).Equals(pwd))) ?? throw Oops.Oh(ErrorCode.E1001);

        if (!user.IsActivated && newPwd != "")
        {
            //user.Password = AESEncryption.Encrypt(newPwd, _authOptions.AesPassword);
            user.Password = PBKDF2Encryption.Encrypt(newPwd);
            user.IsActivated = true;
            user.ActivateTime = DateTime.Now;
            await user.UpdateIncludeNowAsync(new[] { nameof(user.Password), nameof(user.IsActivated), nameof(user.ActivateTime) });
        }

        AuthOutput ret = new()
        {
            OrgName = user.SysOrg.Name,
            Name = user.Name,
            Account = user.Account,
            IsActivated = user.IsActivated,
            AccessToken = "",
        };
        if (!user.IsActivated)
        {
            return ret;
        }

        // 验证账号是否被冻结
        if (user.Status == CommonStatus.DISABLE)
        {
            throw Oops.Oh(ErrorCode.E1003);
        }

        var isSuperAdmin = await roleUserService.IsSuperAdmin(user.Id);

        // 生成Token令牌
        var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
        {
            {ClaimConst.CLAINM_USERID, user.Id},
            {ClaimConst.CLAINM_ACCOUNT, user.Account},
            {ClaimConst.CLAINM_NAME, user.Name},
            {ClaimConst.CLAINM_ORGID, user.SysOrgId},
            {ClaimConst.CLAINM_ORGNAME, user.SysOrg.Name},
            {ClaimConst.CLAINM_SUPERADMIN, isSuperAdmin.ToString()},
        });

        // 设置Swagger自动登录
        httpContextAccessor.HttpContext.SigninToSwagger(accessToken);

        // 生成刷新Token令牌
        var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, App.GetOptions<RefreshTokenSettingOptions>().ExpiredTime);

        // 设置刷新Token令牌
        httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

        ret.AccessToken = accessToken;
        return ret;
    }

    /// <summary>
    /// 账户密码登录
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<AuthOutput> LoginByAccount([Required] LoginInput input)
    {
        return await Login(accountLoginInput: input);
    }

    /// <summary>
    /// 获取当前登录用户信息
    /// </summary>
    /// <returns></returns>
    public async Task<LoginOutput> GetLoginUser()
    {
        var user = await userRep.Include(t => t.SysOrg).FirstOrDefaultAsync(u => u.Id == CurrentSysUserInfo.SysUserId) ?? throw Oops.Oh(ErrorCode.E1002);
        var userId = user.Id;

        var loginOutput = user.Adapt<LoginOutput>();

        var httpContext = httpContextAccessor.HttpContext;

        loginOutput.LastLoginTime = user.LastLoginTime = DateTime.Now;
        loginOutput.LastLoginIp = user.LastLoginIp = httpContext.GetRequestIPv4();

        var client = Parser.GetDefault().Parse(httpContext.Request.Headers["User-Agent"]);
        loginOutput.LastLoginBrowser = client.UA.Family + client.UA.Major;
        loginOutput.LastLoginOs = client.OS.Family + client.OS.Major;

        // 角色信息
        loginOutput.SysRoles = await roleUserService.GetLoginUserRoleList(userId);
        // 权限信息
        loginOutput.Permissions = await menuService.GetLoginPermissionList(userId);
        // 系统所有权限信息
        loginOutput.AllPermissions = await menuService.GetAllPermissionList();
        // 菜单信息
        loginOutput.Menus = await menuService.GetLoginAntMenus(userId);

        // 更新用户最后登录Ip和时间
        await userRep.UpdateIncludeAsync(user, new[] { nameof(SysUser.LastLoginIp), nameof(SysUser.LastLoginTime) });

        // 增加登录日志
        await eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog",
            new SysLogVis
            {
                Name = loginOutput.Name,
                Success = true,
                Message = "登录成功",
                Ip = loginOutput.LastLoginIp,
                Browser = loginOutput.LastLoginBrowser,
                Os = loginOutput.LastLoginOs,
                VisType = LoginType.LOGIN,
                VisTime = loginOutput.LastLoginTime,
                Account = loginOutput.Account
            }));
        return loginOutput;
    }

    /// <summary>
    /// 退出
    /// </summary>
    /// <returns></returns>
    public async Task Logout()
    {
        var ip = httpContextAccessor.HttpContext.GetRequestIPv4();
        httpContextAccessor.HttpContext.SignoutToSwagger();
        //_httpContextAccessor.HttpContext.Response.Headers["access-token"] = "invalid token";

        // 增加退出日志
        await eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog",
            new SysLogVis
            {
                Name = CurrentSysUserInfo.Name,
                Success = true,
                Message = "退出成功",
                VisType = LoginType.LOGOUT,
                VisTime = DateTime.Now,
                Account = CurrentSysUserInfo.Account,
                Ip = ip
            }));
    }

    /// <summary>
    /// 获取验证码
    /// </summary>
    /// <returns></returns>
    public Task<GeneralCaptchaOutput> GetCaptcha()
    {
        // 图片大小要与前端保持一致（坐标范围）
        return Task.FromResult(captchaService.CreateCaptchaImage());
    }

    /// <summary>
    /// 校验验证码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public Task<GeneralCaptchaOutput> VerifyCaptcha(GeneralCaptchaInput input)
    {
        return Task.FromResult(captchaService.CheckCode(input));
    }

    /// <summary>
    /// 获取临时密码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public List<string> GetTempPassword(List<string> input)
    {
        List<string> ret = [];
        foreach (string pwd in input)
        {
            //var npwd = AESEncryption.Encrypt(pwd, _authOptions.AesPassword);
            var npwd = PBKDF2Encryption.Encrypt(pwd);
            ret.Add(npwd);
        }
        return ret;
    }
}