package com.xjrsoft.module.system.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.annotation.XjrLog;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.model.result.R;
import com.xjrsoft.common.model.result.RT;
import com.xjrsoft.common.sms.SmsCtcc;
import com.xjrsoft.common.utils.QrCodeUtil;
import com.xjrsoft.common.utils.RedisUtil;
import com.xjrsoft.config.CommonPropertiesConfig;
import com.xjrsoft.config.KeyCloakConfig;
import com.xjrsoft.module.organization.entity.User;
import com.xjrsoft.module.organization.service.IUserService;
import com.xjrsoft.module.student.dto.QRLoginDto;
import com.xjrsoft.module.system.dto.CaptchaDto;
import com.xjrsoft.module.system.dto.CreateAuthorizeUrlDto;
import com.xjrsoft.module.system.dto.CreateTokenDto;
import com.xjrsoft.module.system.dto.KeyCloakLoginInfoDto;
import com.xjrsoft.module.system.dto.LoginByCodeDto;
import com.xjrsoft.module.system.dto.LoginCaptchaDto;
import com.xjrsoft.module.system.dto.LoginDto;
import com.xjrsoft.module.system.dto.LoginQRCodeDto;
import com.xjrsoft.module.system.service.ILoginService;
import com.xjrsoft.module.system.service.IOauthService;
import com.xjrsoft.module.system.vo.LoginCheckQRCodeVo;
import com.xjrsoft.module.system.vo.LoginQRCodeVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.apache.commons.lang3.RandomUtils;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.representations.AccessTokenResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 登录控制器
 * </p>
 *
 * @author tzx
 * @since 2022-03-02
 */
@Api(tags = "登录模块")
@RestController
@RequestMapping(GlobalConstant.SYSTEM_MODULE_PREFIX)
@AllArgsConstructor
public class LoginController {

    private final ILoginService loginService;
    private final IUserService userService;

    private final RedisUtil redisUtil;

    private KeyCloakConfig keyCloakConfig;

    private final SmsCtcc smsCtcc;

    private IOauthService oauthService;

    private final CommonPropertiesConfig commonPropertiesConfig;

    @PostMapping("/login")
    @ApiOperation(value = "登录", notes = "传入账号:account,密码:password")
    @XjrLog(value = "账号密码登录成功")
    public R login(@RequestBody @Valid LoginDto dto) throws Exception {
        return R.ok(loginService.login(dto));
    }

    @PostMapping("/loginByCode")
    @ApiOperation(value = "登录", notes = "code")
    @XjrLog(value = "Code登录成功")
    public R loginByCode(@RequestBody @Valid LoginByCodeDto dto) throws Exception {
        return R.ok(loginService.loginByCode(dto));
    }

    @GetMapping(value = "/loginQRCode")
    @ApiOperation(value="登录-二维码")
    @SaCheckPermission("login:detail")
    public RT<LoginQRCodeVo> qrcode() {
        long loginCode = IdUtil.getSnowflakeNextId();
        String url = commonPropertiesConfig.getDomainApp() + "pages/login/qrCodeLogin/index?loginCode="+loginCode;
        String active = SpringUtil.getActiveProfile();
        if(!"prod".equals(active)){
            url = "http://yxh-web.ngrok.yingcaibx.com/app/#/pages/login/qrCodeLogin/index?loginCode="+loginCode;
        }
        int width = 200;
        int height = 200;
        int margin = 1;

        try {
            String base64 = QrCodeUtil.createBase64(url, width, height, margin);
            LoginQRCodeVo loginQRCodeVo = new LoginQRCodeVo();
            loginQRCodeVo.setImgBase64(base64);

            redisUtil.set(loginCode + "time", System.currentTimeMillis());
            loginQRCodeVo.setLoginCode(loginCode + "");
            return RT.ok(loginQRCodeVo);
        } catch (Exception e) {
            return RT.error(e.getMessage());
        }
    }

    @PostMapping("/QR-code-login")
    @ApiOperation(value = "二维码登录", notes = "code")
    @XjrLog(value = "二维码登录")
    public RT<String> loginQRCode(@RequestBody LoginQRCodeDto dto) throws Exception {
//        Long timestamp = redisUtil.get(dto.getLoginCode() + "time", Long.class);
//        long timeMillis = System.currentTimeMillis();
//        if(timeMillis - timestamp > 300000){
//            return RT.error("二维码失效，请刷新重试");
//        }
        Boolean b = loginService.loginQRCode(dto);
        if(b){
            return RT.ok("登录成功");
        }

        return RT.ok("登录失败，未能绑定微信公众号");
    }

    @PostMapping("/check-QR-code-login")
    @ApiOperation(value = "验证是否登录成功", notes = "验证是否登录成功")
    @XjrLog(value = "验证是否登录成功")
    public RT<LoginCheckQRCodeVo> checkLoginQRCode(@RequestBody @Valid QRLoginDto dto) {
        Long timestamp = redisUtil.get(dto.getLoginCode() + "time", Long.class);
        if(timestamp == null){
            timestamp = System.currentTimeMillis();
        }
        long timeMillis = System.currentTimeMillis();
        LoginCheckQRCodeVo loginCheckQRCodeVo = new LoginCheckQRCodeVo();
        if(timeMillis - timestamp > 300000){
            loginCheckQRCodeVo.setStatus(1);
            return RT.ok(loginCheckQRCodeVo);
        }

        String token = redisUtil.get(dto.getLoginCode());

        loginCheckQRCodeVo.setToken(token);
        loginCheckQRCodeVo.setStatus(0);
        return RT.ok(loginCheckQRCodeVo);
    }

    @PostMapping("/bindOpenid")
    @ApiOperation(value = "登录", notes = "登录")
    @XjrLog(value = "code换Openid并绑定")
    public R bindOpenid(@RequestBody @Valid LoginByCodeDto dto) throws Exception {
        return R.ok(loginService.bindOpenid(dto));
    }

    @GetMapping("/imgcaptcha")
    @ApiOperation(value = "图形验证码", notes = "图形验证码")
    public R imgCaptcha() {
        return R.ok(loginService.imgCaptcha());
    }

    @PostMapping("/create-token")
    @ApiOperation(value = "创建token", notes = "传入账号:account,密码:password")
    @XjrLog(value = "账号密码登录成功")
    public R createToken(@RequestBody @Valid CreateTokenDto dto) {
        return R.ok(loginService.createToken(dto));
    }


    /**
     * 发送验证码
     */
    @PostMapping("/captcha")
    @XjrLog(value = "发送验证码")
    @ApiOperation(value = "发送验证码", notes = "传入账号:mobile")
    public R captcha(@RequestBody @Valid CaptchaDto captchaDto) {

        String active = SpringUtil.getActiveProfile();
        String code = "111111";

        // 测试环境使用模拟短信，正式环境才发送短信
        if (active.equals("prod")) {
//            String captchaCode = redisUtil.get(captchaDto.getKey(), 0);
//            if (captchaCode == null) {
//                throw new MyException("验证码已过期，请刷新验证码！");
//            }
//
//            if (!captchaCode.equals(captchaDto.getCode())) {
//                throw new MyException("验证码不正确，请刷新验证码！");
//            }
            //生成六位数的字符串
            code = RandomUtils.nextInt(100000, 999999) + StringPool.EMPTY;
            smsCtcc.sendCaptcha(captchaDto.getMobile(), code, true);
        } else {
            smsCtcc.sendCaptcha(captchaDto.getMobile(), code, false);
        }
        return R.ok(Boolean.TRUE);
    }

    /**
     * 验证码登录
     */
    @PostMapping("/loginCaptcha")
    @XjrLog(value = "验证码登录")
    public R loginByCaptcha(@RequestBody LoginCaptchaDto loginCaptchaDto) throws Exception {
        // 验证验证码
        if (!smsCtcc.captchaVerify(loginCaptchaDto.getMobile(), loginCaptchaDto.getCode())) {
            return R.error("验证码不正确！");
        }
        return R.ok(loginService.loginByCaptcha(loginCaptchaDto));
    }


    /**
     * 退出
     */
    @PostMapping("/logout")
    public R logout() {
        StpUtil.logout();
        return R.ok("登出成功！");
    }

    @PostMapping("/token")
    @ApiOperation(value = "根据keycloak-token 登录", notes = "传入keycloak-token")
    @XjrLog(value = "keycloak-token登录成功")
    public R loginByToken(@RequestBody KeyCloakLoginInfoDto dto) {

        Map<String, Object> credentialsMap = new HashMap<>(1);
        credentialsMap.put("secret", keyCloakConfig.getSecret());
        Configuration configuration = new Configuration(keyCloakConfig.getUrl(), keyCloakConfig.getRealm(), keyCloakConfig.getClientId(), credentialsMap, null);
        AuthzClient authzClient = AuthzClient.create(configuration);
        AccessTokenResponse response = authzClient.obtainAccessToken(keyCloakConfig.getUserName(), keyCloakConfig.getPassword());

        if (StrUtil.isNotBlank(response.getError())) {
            return R.error(response.getError());
        }
        //TODO keycloak 登陆过 解析token获取数据 做框架登录操作
        JWT jwt = JWTUtil.parseToken(dto.getToken());
        Object code = jwt.getPayload(keyCloakConfig.getPayload());

        User user = userService.getOne(Wrappers.lambdaQuery(User.class).eq(User::getCode, code));

        if (user == null) {
            return R.error("帐号密码错误！");
        } else if (!Integer.valueOf(1).equals(user.getEnabledMark())) {
            return R.error("该账号已被禁用！");
        }

        //此登录接口登录web端
        StpUtil.login(user.getId(), dto.getDevice());

        SaSession tokenSession = StpUtil.getTokenSession();
        tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user);

        Map<String, Object> vo = new HashMap<>(1);
        vo.put(GlobalConstant.TOKEN_KEY, StpUtil.getTokenValue());

        return R.ok("登录成功！", vo);


    }

    @PostMapping("/qrcode-login")
    @ApiOperation(value = "oauth 扫码登录", notes = "oauth 扫码登录")
    @XjrLog(value = "oauth 扫码登录")
    public R createAuthorizeUrl(@Valid @RequestBody CreateAuthorizeUrlDto dto){

        AuthRequest authRequest = oauthService.getAuthRequest(dto.getSource());
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
        return R.ok(authorizeUrl);
    }
}
