package org.springblade.modules.auth.endpoint;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springblade.auth.enums.ThirdPartyLoginType;
import org.springblade.auth.model.ThirdPartyAuthCallback;
import org.springblade.auth.model.ThirdPartyLogin;
import org.springblade.auth.thirdparty.ThirdPartyAuthRequest;
import org.springblade.common.cache.CacheNames;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.constant.AppConstant;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.WebUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.exception.ServiceException;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.auth.enums.UserEnum;
import org.springblade.modules.auth.provider.ITokenGranter;
import org.springblade.modules.auth.provider.TokenGranterBuilder;
import org.springblade.modules.auth.provider.TokenParameter;
import org.springblade.modules.auth.utils.TokenUtil;
import org.springblade.modules.auth.vo.ThirdLoginAuthVO;
import org.springblade.modules.system.entity.*;
import org.springblade.modules.system.service.*;
import org.springblade.modules.system.vo.SystemBaseInfoVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping(AppConstant.APPLICATION_AUTH_NAME + "/third")
@Api(value = "三方授权登录", tags = "三方授权登录接口")
@Slf4j
public class BladeThirdPartyEndPoint extends BladeController {

    @Autowired
    private IUserService userService;

    @Autowired
    private ITenantService tenantService;

    @Autowired
    private BladeRedis bladeRedis;

    @Autowired
    private ThirdPartyAuthRequest thirdPartyAuthRequest;

    @Autowired
    private ISystemBaseInfoService systemBaseInfoService;

    @Autowired
    private IThirdLoginConfigService thirdLoginConfigService;

    @Autowired
    private IThirdBindCountRecordService thirdBindCountRecordService;

    @GetMapping("/auth-url")
    @ApiOperation("获取三方授权登录生成的二维码链接")
    public String renderAuth(@ApiParam @RequestParam ThirdPartyLoginType loginType,
                             @ApiParam @RequestParam(required = false) String redirectUri) {
        ThirdPartyLogin login = getThirdPartyLogin(loginType,redirectUri);

        return thirdPartyAuthRequest.getAuthorizeUrl(login);
    }

    @GetMapping("/login/{source}")
    @ApiOperation("三方授权登录")
    public Kv thirdLogin(@ApiParam @PathVariable("source") ThirdPartyLoginType source,
                         @ApiParam ThirdLoginAuthVO loginAuthVO) {

        Kv authInfo = Kv.create();
        if (!source.getValue().equals(ThirdPartyLoginType.publicAccount.getValue())) {
            return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_msg", "回调url不能为空");
        }
        loginAuthVO.setLoginType(source);
        // 校验登录方式是否被允许
        SystemBaseInfoVO systemBaseInfoVO = systemBaseInfoService.getSystemBaseInfoVO();
        // 获取三方的身份唯一标识
        String openId = null;
        if (!source.getValue().equals(ThirdPartyLoginType.publicAccount.getValue())) {
            openId = getOpenId(loginAuthVO, loginAuthVO.getRedirectUri());
        }
        ThirdPartyLoginType loginType = loginAuthVO.getLoginType();
        ThirdBindCountRecord countRecord = null;
        if (loginType.getValue().equals(ThirdPartyLoginType.weChat.getValue())) {
            // 微信开放平台
            countRecord = thirdBindCountRecordService.getOne(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                    .eq(ThirdBindCountRecord::getWechatOpenId, openId));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.qiWeChat.getValue())) {
            // 企业微信
            countRecord = thirdBindCountRecordService.getOne(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                    .eq(ThirdBindCountRecord::getQyWechatOpenId, openId));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.dingTalk.getValue())) {
            // 钉钉
            countRecord = thirdBindCountRecordService.getOne(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                    .eq(ThirdBindCountRecord::getDingOpenid, openId));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.feiShu.getValue())) {
            // 飞书
            countRecord = thirdBindCountRecordService.getOne(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                    .eq(ThirdBindCountRecord::getFeiShuOpenId, openId));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.publicAccount.getValue())){
            openId = bladeRedis.get(CacheNames.THIRD_OPENID + loginAuthVO.getTicket());
            if (StrUtil.isNotEmpty(loginAuthVO.getOpenid())) {
                openId = loginAuthVO.getOpenid();
            }
            // 公众号
            countRecord = thirdBindCountRecordService.getOne(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                    .eq(ThirdBindCountRecord::getPublicAccountOpenid, openId));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.applet.getValue())) {
            // 小程序
            countRecord = thirdBindCountRecordService.getOne(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                    .eq(ThirdBindCountRecord::getMicroAppOpenId, openId)
                    .or().eq(ThirdBindCountRecord::getMicroAppUnionId, openId));
        }
        // 保存接口
        String openIdKey = UUID.randomUUID().toString();
        bladeRedis.setEx(CacheNames.THIRD_OPENID + openIdKey, openId, Duration.ofMinutes(10));
        if (countRecord == null) {
            return authInfo.set("error_code", HttpServletResponse.SC_NO_CONTENT)
                    .set("openIdKey",openIdKey)
                    .set("error_msg", "用户不存在,请绑定账号...");
        }

        UserInfo userInfo = grant(userService.getById(countRecord.getUserId()));

        if (userInfo == null || userInfo.getUser() == null) {
            return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_msg", "用户名或密码不正确");
        }

        if (Func.isEmpty(userInfo.getRoles())) {
            return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_msg", "未获得用户的角色信息");
        }

        String loginTypes = systemBaseInfoVO.getLoginType();
        // 判断当前的多登录方式是否为第一个登录方式
//        if (loginTypes.split(",").length > 1 && (loginTypes.split(",")[0].equals("4")
//                    || loginTypes.split(",")[0].equals("5")
//                    || loginTypes.split(",")[0].equals("6")
//                    || loginTypes.split(",")[0].equals("7"))) {
//
//            if (loginTypes.split(",")[1].equals("2")) {
//                String phone = userInfo.getUser().getPhone();
//                String key = UUID.randomUUID().toString();
//                bladeRedis.setEx(CacheNames.PHONE_KEY + key, phone, Duration.ofMinutes(30));
//                String s = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
//                userInfo.getUser().setPhone(s);
//                return authInfo.set("phone_key", key).set("phone", s);
//            } else {
//                return authInfo.set("ok", true);
//            }
//        }
        return TokenUtil.createAuthInfo(userInfo);

    }

    @PostMapping("/bind/openid")
    @ApiOperation(value = "绑定租户三方账号，并获取令牌", notes = "传入租户ID:tenantId,账号:account,密码:password," +
            "三方登录的KEY:header['openId-Key']")
    public Kv bindOpenId(@ApiParam(value = "租户ID", required = true) @RequestParam String tenantId,
                         @ApiParam(value = "微信公众号二维码提供的ticket") @RequestParam(required = false) String ticket,
                         @ApiParam(value = "手机授权登录获取的openid") @RequestParam(required = false) String openid,
                         @ApiParam(value = "账号", required = true) @RequestParam(required = false) String username,
                         @ApiParam(value = "密码", required = true) @RequestParam(required = false) String password,
                         @ApiParam(value = "三方类别",required = true) @RequestParam ThirdPartyLoginType loginType){
        log.info("绑定三方账户开始了.....");
        Kv authInfo = Kv.create();

        String grantType = WebUtil.getRequest().getParameter("grant_type");
        String openIdKey = WebUtil.getRequest().getHeader(TokenUtil.OPENID_KEY);
        log.info("前端传递的openIdKey: "+openIdKey);

        String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);

        TokenParameter tokenParameter = new TokenParameter();
        tokenParameter.getArgs().set("tenantId", tenantId).set("username", username).set("password", password).set("grantType", grantType).set("userType", userType);

        ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
        UserInfo userInfo = granter.grant(tokenParameter);

        if (userInfo == null || userInfo.getUser() == null) {
            return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "用户名或密码不正确");
        }

        if (userInfo.getUser().getIsLogin() == 1) {
            return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "该用户不能使用账号密码登录");
        }

        if (Func.isEmpty(userInfo.getRoles())) {
            return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "未获得用户的角色信息");
        }

        String openId = bladeRedis.get(CacheNames.THIRD_OPENID + openIdKey);
        log.info("获取缓存的openId：key："+CacheNames.THIRD_OPENID + openIdKey+" value: "+openId);
        User user = userInfo.getUser();
        if (StrUtil.isNotEmpty(openid)) {
            openId = openid;
        }
        // 绑定三方账号
        boolean isBind = bindThirdOpenId(openId, user, loginType,ticket);
        // 校验登录方式是否被允许
        SystemBaseInfoVO systemBaseInfoVO = systemBaseInfoService.getSystemBaseInfoVO();
        if (isBind){
            log.info("账户绑定成功...");
            bladeRedis.del(CacheNames.THIRD_OPENID + openIdKey);
            String loginTypes = systemBaseInfoVO.getLoginType();
            // 判断当前的多登录方式是否为第一个登录方式
//            if (loginTypes.split(",").length > 1 && (loginTypes.split(",")[0].equals("4")
//                    || loginTypes.split(",")[0].equals("5")
//                    || loginTypes.split(",")[0].equals("6")
//                    || loginTypes.split(",")[0].equals("7"))) {
//
//                if (loginTypes.split(",")[1].equals("2")) {
//                    String phone = userInfo.getUser().getPhone();
//                    String key = UUID.randomUUID().toString();
//                    bladeRedis.setEx(CacheNames.PHONE_KEY + key, phone, Duration.ofMinutes(30));
//                    String s = phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
//                    userInfo.getUser().setPhone(s);
//                    return authInfo.set("phone_key", key).set("phone", s);
//                } else {
//                    return authInfo.set("ok", true);
//                }
//            }
            return TokenUtil.createAuthInfo(userInfo);
        } else {
            log.info("账户绑定失败");
            return authInfo.set("error_code",HttpServletResponse.SC_RESET_CONTENT).set("error_description", "重复绑定...");
        }
    }

    @GetMapping("/bind/{source}")
    @ApiOperation("三方授权登录绑定")
    public Kv thirdBindLogin(@ApiParam @PathVariable("source") ThirdPartyLoginType source,
                             @ApiParam ThirdLoginAuthVO loginAuthVO) {

        loginAuthVO.setLoginType(source);
        // 校验登录方式是否被允许
        Kv authInfo = Kv.create();
        String openId = null;
        if (!source.getValue().equals(ThirdPartyLoginType.publicAccount.getValue())) {
           openId = getOpenId(loginAuthVO, loginAuthVO.getRedirectUri());
        }
        // 绑定三方账号
        BladeUser bladeUser = getUser();
        if (bladeUser == null) {
            throw new ServiceException("请先登录再绑定");
        }
        User user = new User();
        user.setId(bladeUser.getUserId());
        ThirdPartyLoginType loginType = loginAuthVO.getLoginType();
        if (StrUtil.isNotEmpty(loginAuthVO.getOpenid())) {
            openId = loginAuthVO.getOpenid();
        }
        boolean isBind = bindThirdOpenId(openId, user, loginType, loginAuthVO.getTicket());
        // 绑定三方账号
        if (isBind) {
            return authInfo.set("ok", "绑定成功...");
        } else {
            return authInfo.set("error_code", HttpServletResponse.SC_RESET_CONTENT)
                    .set("error_description", "重复绑定...");
        }
    }

    @PostMapping("/unBind")
    @ApiOperation("解绑三方账号")
    public Kv unBindThirdCount(ThirdPartyLoginType loginType){
        if (getUser() == null){
            throw new ServiceException("请先登录再解除绑定");
        }
        ThirdBindCountRecord countRecord = new ThirdBindCountRecord();
        countRecord.setUserId(getUser().getUserId());
        boolean unBind = true;
        if (loginType.getValue().equals(ThirdPartyLoginType.weChat.getValue())) {
            countRecord.setWechatOpenId("");
            unBind = thirdBindCountRecordService.update(countRecord,
                    Wrappers.<ThirdBindCountRecord>lambdaUpdate()
                            .eq(ThirdBindCountRecord::getUserId,getUser().getUserId()));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.dingTalk.getValue())) {
            countRecord.setDingOpenid("");
            unBind = thirdBindCountRecordService.update(countRecord,
                    Wrappers.<ThirdBindCountRecord>lambdaUpdate()
                            .eq(ThirdBindCountRecord::getUserId,getUser().getUserId()));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.feiShu.getValue())) {
            countRecord.setFeiShuOpenId("");
            unBind = thirdBindCountRecordService.update(countRecord,
                    Wrappers.<ThirdBindCountRecord>lambdaUpdate()
                            .eq(ThirdBindCountRecord::getUserId,getUser().getUserId()));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.qiWeChat.getValue())){
            countRecord.setQyWechatOpenId("");
            unBind = thirdBindCountRecordService.update(countRecord,
                    Wrappers.<ThirdBindCountRecord>lambdaUpdate()
                            .eq(ThirdBindCountRecord::getUserId,getUser().getUserId()));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.publicAccount.getValue())){
            // 公众号
            countRecord.setPublicAccountOpenid("");
            unBind = thirdBindCountRecordService.update(countRecord,
                    Wrappers.<ThirdBindCountRecord>lambdaUpdate()
                            .eq(ThirdBindCountRecord::getUserId,getUser().getUserId()));
        } else if (loginType.getValue().equals(ThirdPartyLoginType.applet.getValue())) {
            // 小程序
            countRecord.setMicroAppOpenId("");
            countRecord.setMicroAppUnionId("");
            unBind = thirdBindCountRecordService.update(countRecord,
                    Wrappers.<ThirdBindCountRecord>lambdaUpdate()
                            .eq(ThirdBindCountRecord::getUserId,getUser().getUserId()));
        }
        if (unBind) {
            return Kv.create().set("ok", "解绑成功");
        } else {
            return Kv.create().set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "解绑失败");
        }
    }

    /**
     * 绑定三方账号
     * @param openId
     * @param user
     * @param loginType
     */
    private boolean bindThirdOpenId(String openId, User user, ThirdPartyLoginType loginType,String sceneStr) {
        boolean isBind = false;
        ThirdBindCountRecord countRecord = thirdBindCountRecordService.getOne(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                .eq(ThirdBindCountRecord::getUserId, user.getId()));
        if (loginType.getValue().equals(ThirdPartyLoginType.weChat.getValue())) {
            if (countRecord != null && countRecord.getId() > 0) {
                if (StrUtil.isEmpty(countRecord.getWechatOpenId())) {
                    countRecord.setWechatOpenId(openId);
                    isBind = thirdBindCountRecordService.updateById(countRecord);
                }
            } else {
                countRecord = new ThirdBindCountRecord();
                countRecord.setWechatOpenId(openId);
                countRecord.setUserId(user.getId());
                isBind = thirdBindCountRecordService.save(countRecord);
            }
        } else if (loginType.getValue().equals(ThirdPartyLoginType.dingTalk.getValue())) {
            if (countRecord != null && countRecord.getId() > 0) {
                if (StrUtil.isEmpty(countRecord.getDingOpenid())) {
                    countRecord.setDingOpenid(openId);
                    isBind = thirdBindCountRecordService.updateById(countRecord);
                }
            } else {
                countRecord = new ThirdBindCountRecord();
                countRecord.setDingOpenid(openId);
                countRecord.setUserId(user.getId());
                isBind = thirdBindCountRecordService.save(countRecord);
            }
        } else if (loginType.getValue().equals(ThirdPartyLoginType.feiShu.getValue())) {
            if (countRecord != null && countRecord.getId() > 0) {
                if (StrUtil.isEmpty(countRecord.getFeiShuOpenId())) {
                    countRecord.setFeiShuOpenId(openId);
                    isBind = thirdBindCountRecordService.updateById(countRecord);
                }
            } else {
                countRecord = new ThirdBindCountRecord();
                countRecord.setFeiShuOpenId(openId);
                countRecord.setUserId(user.getId());
                isBind = thirdBindCountRecordService.save(countRecord);
            }
        } else if (loginType.getValue().equals(ThirdPartyLoginType.qiWeChat.getValue())) {
            if (countRecord != null && countRecord.getId() > 0) {
                if (StrUtil.isEmpty(countRecord.getQyWechatOpenId())) {
                    countRecord.setQyWechatOpenId(openId);
                    isBind = thirdBindCountRecordService.updateById(countRecord);
                }
            } else {
                countRecord = new ThirdBindCountRecord();
                countRecord.setQyWechatOpenId(openId);
                countRecord.setUserId(user.getId());
                isBind = thirdBindCountRecordService.save(countRecord);
            }
        } else if (loginType.getValue().equals(ThirdPartyLoginType.publicAccount.getValue())){
            String openIds = bladeRedis.get(CacheNames.THIRD_OPENID + sceneStr);
            if (StrUtil.isNotEmpty(openId)) {
                openIds = openId;
            }
            // 公众号
            if (countRecord != null && countRecord.getId() > 0) {
                if (StrUtil.isEmpty(countRecord.getPublicAccountOpenid())) {
                    countRecord.setPublicAccountOpenid(openIds);
                    isBind = thirdBindCountRecordService.updateById(countRecord);
                }
            } else {
                countRecord = new ThirdBindCountRecord();
                countRecord.setPublicAccountOpenid(openIds);
                countRecord.setUserId(user.getId());
                isBind = thirdBindCountRecordService.save(countRecord);
            }
        } else if (loginType.getValue().equals(ThirdPartyLoginType.applet.getValue())) {
            // 小程序
            if (countRecord != null && countRecord.getId() > 0) {
                if (StrUtil.isEmpty(countRecord.getMicroAppOpenId())) {
                    countRecord.setMicroAppOpenId(openId);
                    isBind = thirdBindCountRecordService.updateById(countRecord);
                }
            } else {
                countRecord = new ThirdBindCountRecord();
                countRecord.setMicroAppOpenId(openId);
                countRecord.setUserId(user.getId());
                isBind = thirdBindCountRecordService.save(countRecord);
            }
        }

        return isBind;
    }

    private ThirdPartyLogin getThirdPartyLogin(ThirdPartyLoginType loginType,String redirectUri) {

        List<ThirdLoginConfig> list = thirdLoginConfigService.list(Wrappers.<ThirdLoginConfig>lambdaQuery()
                .eq(ThirdLoginConfig::getLoginType, loginType.getValue())
                .eq(ThirdLoginConfig::getStatus, 1));
        ThirdPartyLogin login = new ThirdPartyLogin();
        if (CollectionUtil.isNotEmpty(list)) {
            ThirdLoginConfig loginConfig = list.get(0);
            login = JSONUtil.toBean(loginConfig.getLoginConf(),ThirdPartyLogin.class);
            login.setRedirectUri(redirectUri);
            login.setLoginType(loginType);
        }

        return login;
    }

    public UserInfo grant(User user) {
        String tenantId = user.getTenantId();
        String username = user.getAccount();
        String password = user.getPassword();
        UserInfo userInfo = null;
        if (StrUtil.isNotBlank(username) && StrUtil.isNotBlank(password)) {
            // 获取租户信息
            Tenant tenant = tenantService.getByTenantId(tenantId);
            if (TokenUtil.judgeTenant(tenant)) {
                throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
            }

            // 根据不同用户类型调用对应的接口返回数据，用户可自行拓展
            // 获取用户类型
            String userType = Func.toStr(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
            if (userType.equals(UserEnum.WEB.getName())) {
                userInfo = userService.userInfo(tenantId, username, password, UserEnum.WEB);
            } else if (userType.equals(UserEnum.APP.getName())) {
                userInfo = userService.userInfo(tenantId, username, password, UserEnum.APP);
            } else {
                userInfo = userService.userInfo(tenantId, username, password, UserEnum.OTHER);
            }
        }
        return userInfo;
    }

    private String getOpenId(ThirdLoginAuthVO loginAuthVO, String redirectUri) {
        ThirdPartyAuthCallback callback = new ThirdPartyAuthCallback();
        BeanUtil.copyProperties(loginAuthVO, callback);
        ThirdPartyLogin thirdPartyLogin = getThirdPartyLogin(loginAuthVO.getLoginType(),redirectUri);
        String openId = thirdPartyAuthRequest.login(thirdPartyLogin, callback,null);
        if (StrUtil.isEmpty(openId)) {
            throw new ServiceException("三方登录失败,请检查配置");
        }

        return openId;
    }


   @GetMapping("/bind/status")
   @ApiOperation("查询当前登录人员的三方账号绑定情况")
    public R<List<ThirdBindCountRecord>> getIsBindUser(){
       BladeUser user = getUser();
       if (user != null){
            return R.data(thirdBindCountRecordService.list(Wrappers.<ThirdBindCountRecord>lambdaQuery()
                   .eq(ThirdBindCountRecord::getUserId,user.getUserId())));
       } else {
           return null;
       }
   }
}
