package com.yonge.cooleshow.auth.core.provider;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.alibaba.fastjson.JSON;
import com.microsvc.toolkit.config.jwt.utils.RsaKeyHelper;
import com.microsvc.toolkit.middleware.wechat.WxServiceManager;
import com.yonge.cooleshow.auth.api.dto.SysUserInfo;
import com.yonge.cooleshow.auth.api.entity.LoginEntity;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.auth.config.token.PhoneAuthenticationToken;
import com.yonge.cooleshow.auth.enums.ELoginType;
import com.yonge.cooleshow.auth.middleware.wechat.WxCacheService;
import com.yonge.cooleshow.auth.service.SysUserDeviceService;
import com.yonge.cooleshow.auth.service.SysUserService;
import com.yonge.cooleshow.common.security.SecurityConstants;
import com.yonge.cooleshow.common.service.IdGeneratorService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.transaction.annotation.Transactional;

import java.security.interfaces.RSAPublicKey;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Objects;

@Slf4j
public class PhoneAuthenticationProvider extends AbstractAuthenticationProvider {

    private UserDetailsService userDetailsService;

    private IdGeneratorService smsCodeService;

    private SysUserService sysUserService;

    private SysUserDeviceService sysUserDeviceService;

    private WxCacheService wxCacheService;

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, Authentication authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(this.messages.getMessage("PhoneAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserDetails retrieveUser(String username, Authentication authentication) throws AuthenticationException {
        LoginEntity loginEntity = (LoginEntity) authentication.getCredentials();
        if (loginEntity == null) {
            throw new BadCredentialsException("Bad credentials");
        }

        String smsCode = loginEntity.getSmsCode();
        String phone = loginEntity.getPhone();
        // 二维码登录
        String qrCode = loginEntity.getQrCode();
        // 授权Token登录
        String authToken = loginEntity.getAuthToken();
        // 用户登陆方式
        ELoginType loginType = ELoginType.get(loginEntity.getLoginType());

        if (ELoginType.WECHAT_MA == loginType) {
            // 小程序登陆
            // 根据小程序code获取openId；查询用户是否存在
            // 查询配置信息, keyword =>小程序apppid
            WxMaService wxMaService = WxServiceManager.getInstance().getWxMaService(phone, wxCacheService);
            if (wxMaService == null) {
                log.warn("genRequestAuthorityTokenInfo WX_APPID, appid={}, jscode={}", phone, smsCode);
                throw new BadCredentialsException("小程序授权失败，请联系管理员");
            }

            String openid;
            try {
                // 校验请求jscode的合法
                WxMaJscode2SessionResult sessionret = wxMaService.getUserService().getSessionInfo(smsCode);

                if (StringUtils.isAnyBlank(sessionret.getOpenid(), sessionret.getSessionKey())) {
                    log.warn("genRequestAuthorityTokenInfo JSCODE, ret={}", JSON.toJSONString(sessionret));
                    throw new BadCredentialsException("小程序授权失败，请重新授权");
                }
                // 用户openid
                openid = sessionret.getOpenid();

            } catch (Exception e) {
                log.error("genRequestAuthorityTokenInfo WX_OAUTH2, appid={}, jscode={}", phone, smsCode, e);
                throw new BadCredentialsException("小程序授权已失效,请重新登陆");
            }

            String clientId = loginEntity.getClientId();
            String deviceNum = loginEntity.getDeviceNum();
            // 根据用户授权openid，查询机构员工绑定信息
            SysUser sysUser = sysUserService.getSysUserByOpenId(openid, clientId);
            if (Objects.isNull(sysUser)) {
                // 小程序未绑定账号
                throw new UsernameNotFoundException("MA:5001:" + openid);
            }

            // 重置登陆账号信息
            username = MessageFormat.format("{0}:{1}:{2}", SecurityConstants.MA_PRINCIPAL_PREFIX, openid, sysUser.getPhone());

            // 绑定设备
            if (StringUtils.isNotBlank(deviceNum)) {
                sysUserDeviceService.bindDevice(clientId, sysUser.getId(), deviceNum);
            }

        } else {

            // 其他登陆方式
            if (StringUtils.isNotEmpty(qrCode)) {
                // 二维码验证
                boolean b = sysUserService.verifyQrCode(phone, qrCode);
                if (!b) {
                    throw new BadCredentialsException("二维码校验失败");
                }
            } else if (StringUtils.isNotEmpty(authToken)) {
                // 授权authToken登录
                try {
                    RSAPublicKey rsaPublicKey = RsaKeyHelper.getRSAPublicKey("jmedu", "dayaedu", "jmedu.jks", "dayaedu");
                    Jwt jwt = JwtHelper.decodeAndVerify(authToken, new RsaVerifier(rsaPublicKey));

                    //获取jwt原始内容
                    String claims = jwt.getClaims();
                    if (StringUtils.isEmpty(claims)) {
                        throw new BadCredentialsException("三方授权校验失败");
                    }
                    log.info("retrieveUser claims={}", claims);
                } catch (Exception e) {
                    log.error("retrieveUser authToken={}", authToken, e);
                }

            } else {
                // 验证码验证
                boolean b = smsCodeService.verifyValidCode(phone, smsCode, "SMS_VERIFY_CODE_LOGIN");
                if (!b) {
                    throw new BadCredentialsException("验证码校验失败");
                }
            }

            String clientId = loginEntity.getClientId();
            Boolean isRegister = loginEntity.getIsSurportRegister();
            String loginUserType = loginEntity.getLoginUserType();
            String deviceNum = loginEntity.getDeviceNum();

            SysUserInfo userInfo = sysUserService.queryUserInfoByPhone(phone);

            if (userInfo == null) {
                if (isRegister == false || StringUtils.equals("SYSTEM", clientId)) {
                    throw new LockedException("用户不存在");
                }

                userInfo = sysUserService.registerUser(loginEntity.getPhone(), clientId, loginUserType);

                if (Objects.nonNull(userInfo.getSysUser())) {
                    // 自动添加系统默认IM帐号为好友，并自动发送通知消息
                    sysUserService.sendSysCustomerServiceFriendMessage(userInfo.getSysUser(), clientId.toUpperCase());
                }

                if (StringUtils.isNotBlank(deviceNum)) {
                    sysUserDeviceService.bindDevice(clientId, userInfo.getSysUser().getId(), deviceNum);
                }
            } else {
                SysUser user = userInfo.getSysUser();
                if (user == null) {
                    throw new LockedException("用户不存在");
                }
                if (user.getLockFlag() == 1) {
                    throw new LockedException("用户已锁定");
                }

                if (StringUtils.isNotBlank(deviceNum)) {
                    sysUserDeviceService.bindDevice(clientId, user.getId(), deviceNum);
                }

                // 学生账号修改隐藏为显示
                if (StringUtils.equalsIgnoreCase(loginUserType, "STUDENT")
                        || StringUtils.equalsIgnoreCase(clientId, "STUDENT")) {
                    sysUserService.updateStudentHideFlag(userInfo.getSysUser().getId(), 0);
                }

                //登录
                if (userInfo.getSysUser().getUserType().contains(clientId)){
                    return login(username);
                }
                //官网登录
                if(StringUtils.isNotEmpty(loginUserType) && userInfo.getSysUser().getUserType().contains(loginUserType)){
                    return login(username);
                }

                /**********************************注册*********************************************/
                //不能注册的
                if(isRegister == false || StringUtils.equals("SYSTEM", clientId)){
                    throw new LockedException("用户不存在");
                }

                // 同账号，用户信息存在的情况
                if (!user.getUserType().contains(clientId.toUpperCase())) {
                    // 自动添加系统默认IM帐号为好友，并自动发送通知消息
                    sysUserService.sendSysCustomerServiceFriendMessage(userInfo.getSysUser(), clientId.toUpperCase());
                }

                user.setUpdateTime(new Date());
                if(StringUtils.isNotEmpty(loginUserType)){
                    if (StringUtils.equalsIgnoreCase(loginUserType, "TEACHER")) {
                        user.setUserType(user.getUserType() + "," + loginUserType);
                        sysUserService.saveTeacher(user);
                    } else if (StringUtils.equalsIgnoreCase(loginUserType, "STUDENT")) {
                        user.setUserType(user.getUserType() + "," + loginUserType);
                        sysUserService.saveStudent(user);
                    }else {
                        throw new LockedException("用户不存在");
                    }
                }else if(StringUtils.isNotEmpty(clientId)){
                    if (StringUtils.equalsIgnoreCase(clientId, "TEACHER")) {
                        user.setUserType(user.getUserType() + "," + clientId);
                        sysUserService.saveTeacher(user);
                    } else if (StringUtils.equalsIgnoreCase(clientId, "STUDENT")) {
                        user.setUserType(user.getUserType() + "," + clientId);
                        sysUserService.saveStudent(user);
                    } else {
                        throw new LockedException("用户不存在");
                    }
                }else{
                    throw new LockedException("用户不存在");
                }
                sysUserService.update(user);
            }

        }

        return login(username);
    }

    private UserDetails login(String username){
        UserDetails loadedUser;
        try {
            loadedUser = userDetailsService.loadUserByUsername(username);
        } catch (UsernameNotFoundException e) {
            throw e;
        } catch (Exception e) {
            throw new InternalAuthenticationServiceException(e.getMessage(), e);
        }

        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException("账户不存在");
        } else {
            return loadedUser;
        }
    }

    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        PhoneAuthenticationToken result = new PhoneAuthenticationToken(principal, authentication.getCredentials(), user.getAuthorities());
        result.setDetails(authentication.getDetails());
        return result;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return PhoneAuthenticationToken.class.isAssignableFrom(aClass);
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void setSysUserService(SysUserService sysUserService) {
        this.sysUserService = sysUserService;
    }

    public void setSmsCodeService(IdGeneratorService smsCodeService) {
        this.smsCodeService = smsCodeService;
    }

    public void setSysUserDeviceService(SysUserDeviceService sysUserDeviceService) {
        this.sysUserDeviceService = sysUserDeviceService;
    }

    public void setWxCacheService(WxCacheService wxCacheService) {
        this.wxCacheService = wxCacheService;
    }
}
