package com.zhiqiyun.open.mvc.manager.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhiqiyun.open.annotation.Permission;
import com.zhiqiyun.open.core.enmus.AccountType;
import com.zhiqiyun.open.core.enmus.PasswordType;
import com.zhiqiyun.open.core.enmus.UserLoginLogState;
import com.zhiqiyun.open.core.models.user.*;
import com.zhiqiyun.open.core.service.CaptchaService;
import com.zhiqiyun.open.core.service.OauthService;
import com.zhiqiyun.open.core.service.UserBaseInfoService;
import com.zhiqiyun.open.core.service.UserLoginLogService;
import com.zhiqiyun.open.mvc.Result;
import com.zhiqiyun.open.mvc.manager.params.ChangePasswordParam;
import com.zhiqiyun.open.mvc.manager.params.LoginParam;
import com.zhiqiyun.open.mvc.manager.params.QueryPageParams;
import com.zhiqiyun.open.mvc.manager.params.SaveUserBaseInfoParam;
import com.zhiqiyun.open.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;

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

@Slf4j
@RestController
@RequestMapping("/oauth")
public class OauthController {

    @Autowired
    private OauthService oauthService;

    @Autowired
    private UserBaseInfoService userBaseInfoService;

    @Autowired
    private UserLoginLogService userLoginLogService;

    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @PostMapping("/getCaptcha")
    public Result captcha() {
        String token = ServletContext.getAccessToken();
        if (StringUtils.isBlank(token) || !Token.checkToken(token)) {
            token = Token.init(ServletContext.getRemoteIPAddress()).toTokenString();
        }
        String captcha = captchaService.generateCaptchaBase64(token);
        Map<String, String> dataMap = new HashMap<>();
        dataMap.put("token", token);
        dataMap.put("captcha", captcha);
        if (StringUtils.isNotBlank(captcha)) {
            return Result.instance(Result.Code.SUCCESS).setData(dataMap);
        } else {
            return Result.instance(Result.Code.SERVER_ERROR, "获取验证码失败");
        }
    }

    @PostMapping("/login")
    public Result login(@Valid @RequestBody LoginParam loginParam) {
        UserLoginLog userLoginLog = new UserLoginLog();
        try {
            AccountType accountType = null;
            if (PatternUtils.validExpression(loginParam.getLoginAccount(), PatternUtils.MOBILE_EXPRESSION)) {
                accountType = AccountType.mp;
            }
            if (PatternUtils.validExpression(loginParam.getLoginAccount(), PatternUtils.EMAIL_EXPRESSION)) {
                accountType = AccountType.email;
            }
            userLoginLog.setLoginAccount(loginParam.getLoginAccount());
            userLoginLog.setAccountType(accountType);

            boolean compareCaptchaResult = this.captchaService.compareCaptcha(loginParam.getCaptcha(), loginParam.getToken(), true);
            if (!compareCaptchaResult) {
                userLoginLog.setState(UserLoginLogState.CAPTCHA_ERROR);
                return Result.instance("验证码错误，请重新输入");
            }

            UserLoginAccount userLoginAccount = this.userBaseInfoService.findLoginAccount(loginParam.getLoginAccount(), accountType);
            if (userLoginAccount == null) {
                userLoginLog.setState(UserLoginLogState.ACCOUNT_NOT_EXIST);
                return Result.instance("用户名或密码错误，请重新输入");
            } else {
                UserBaseInfo userBaseInfo = this.userBaseInfoService.getById(userLoginAccount.getUid());

                UserPassword userPassword = this.userBaseInfoService.findPassword(userBaseInfo.getId(), PasswordType.login);

                String loginPassword = this.oauthService.generatePassword(loginParam.getLoginPassword(), userPassword.getSalt());
                if (loginPassword.equals(userPassword.getPasswd())) {

                    List<String> listAuthorities = this.userBaseInfoService.findUserAuthorities(userBaseInfo.getId());

                    String accessToken = Token.init(ServletContext.getRemoteIPAddress()).toTokenString();
                    OauthInfo oauthInfo = new OauthInfo()
                            .setId(userBaseInfo.getId())
                            .setAccessToken(accessToken)
                            .setNickName(userBaseInfo.getNickName())
                            .setAvatar(userBaseInfo.getAvatar())
                            .setLoginTime(DateUtil.current())
                            .setGender(userBaseInfo.getGender())
                            .setAuthorities(listAuthorities);

                    this.oauthService.setAuth(accessToken, oauthInfo);
                    userLoginLog.setState(UserLoginLogState.SUCCESS);
                    return Result.instance(Result.Code.SUCCESS).setData(oauthInfo);
                } else {
                    userLoginLog.setState(UserLoginLogState.PASSWORD_ERROR);
                    return Result.instance("用户名或密码错误，请重新输入");
                }
            }
        } catch (Exception e) {
            log.error("", e);
            userLoginLog.setState(UserLoginLogState.LOGIN_EXCEPTION);
            return Result.instance("系统异常,请稍后再试");
        } finally {
            userLoginLog.setLoginIp(ServletContext.getRemoteIPAddress());
            userLoginLog.setFromSource(ServletContext.getFromSource());
            userLoginLog.setDeviceId("");
            userLoginLog.setLoginTime(DateUtil.current());
            userLoginLog.setLoginLocal("未知地址");
            this.threadPoolTaskExecutor.execute(() -> userLoginLogService.save(userLoginLog));
        }
    }


    @Permission(tags = "获取用户登录信息", writeLog = false)
    @PostMapping("/getOauthInfo")
    public Result getOauthInfo() {
        String accessToken = ServletContext.getAccessToken();
        OauthInfo oauthInfo = oauthService.getAuth(accessToken);
        if (oauthInfo != null) {

            UserBaseInfo userBaseInfo = this.userBaseInfoService.getById(oauthInfo.getId());
            List<String> listAuthorities = this.userBaseInfoService.findUserAuthorities(userBaseInfo.getId());

            oauthInfo.setNickName(userBaseInfo.getNickName())
                    .setAvatar(userBaseInfo.getAvatar())
                    .setLoginTime(DateUtil.current())
                    .setGender(userBaseInfo.getGender())
                    .setAuthorities(listAuthorities);
            this.oauthService.setAuth(accessToken, oauthInfo);
        }
        return Result.instance(Result.Code.SUCCESS).setData(oauthInfo);
    }

    @PostMapping("/loginOut")
    public Result loginOut() {
        String accessToken = ServletContext.getAccessToken();
        if (StringUtils.isNotBlank(accessToken)) {
            this.oauthService.destroy(accessToken);
        }
        return Result.instance(Result.Code.SUCCESS);
    }


    @Permission(tags = "获取个人登录账号", writeLog = false)
    @GetMapping("/getLoginAccounts")
    public Result getLoginAccounts() {
        String accessToken = ServletContext.getAccessToken();
        OauthInfo oauthInfo = oauthService.getAuth(accessToken);
        Map<String, Object> dataMap = new HashMap<>();
        List<UserLoginAccount> loginAccounts = this.userBaseInfoService.findLoginAccounts(oauthInfo.getId());
        for (UserLoginAccount account : loginAccounts) {
            dataMap.put(account.getAccountType().name(), account.getLoginAccount());
        }
        return Result.instance(Result.Code.SUCCESS).setData(dataMap);
    }

    @Permission(tags = "获取个人基本信息", writeLog = false)
    @GetMapping("/getUserBaseInfo")
    public Result getUserBaseInfo() {
        String accessToken = ServletContext.getAccessToken();
        OauthInfo oauthInfo = oauthService.getAuth(accessToken);
        UserBaseInfo userBaseInfo = this.userBaseInfoService.getById(oauthInfo.getId());
        return Result.instance(Result.Code.SUCCESS).setData(userBaseInfo);
    }


    @Permission(tags = "保存个人基本信息")
    @PostMapping("/saveUserBaseInfo")
    public Result saveUserBaseInfo(@Valid @RequestBody SaveUserBaseInfoParam param) {
        String accessToken = ServletContext.getAccessToken();
        OauthInfo oauthInfo = oauthService.getAuth(accessToken);
        UserBaseInfo userBaseInfo = new UserBaseInfo();
        BeanUtils.copyProperties(param, userBaseInfo);
        this.userBaseInfoService.updateBaseInfo(oauthInfo.getId(), userBaseInfo);
        return Result.instance(Result.Code.MESSAGE_SUCCESS);
    }

    @Permission(tags = "获取用户登录记录", writeLog = false)
    @PostMapping("/getLoginLogs")
    public Result getLoginLogs(@RequestBody QueryPageParams params) {

        Page<UserLoginLog> page = params.getPage();
        page.addOrder(OrderItem.desc("login_time"));

        String accessToken = ServletContext.getAccessToken();
        OauthInfo oauthInfo = oauthService.getAuth(accessToken);

        QueryWrapper<UserLoginLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uid", oauthInfo.getId());

        Page<UserLoginLog> pageResult = this.userLoginLogService.page(page, queryWrapper);

        return Result.instance(Result.Code.SUCCESS).setData(pageResult);
    }

    @Permission(tags = "用户自己修改密码")
    @PostMapping("/changePassword")
    public Result changePassword(@Valid @RequestBody ChangePasswordParam param) {
        String accessToken = ServletContext.getAccessToken();
        OauthInfo oauthInfo = oauthService.getAuth(accessToken);

        if (!param.getNewPassword().equals(param.getConfirmNewPassword())) {
            return Result.instance("新密码与确认新密码不一致");
        }
        UserPassword userPassword = this.userBaseInfoService.findPassword(oauthInfo.getId(), PasswordType.login);
        String loginPassword = this.oauthService.generatePassword(param.getLoginPassword(), userPassword.getSalt());
        if (loginPassword.equals(userPassword.getPasswd())) {
            String salt = RandomUtil.getSalt();
            String newLoginPassword = this.oauthService.generatePassword(param.getNewPassword(), salt);

            this.userBaseInfoService.updatePassword(oauthInfo.getId(), newLoginPassword, salt, PasswordType.login);
            return Result.instance(Result.Code.MESSAGE_SUCCESS, "修改密码成功，下次请用新密码登录");

        } else {
            return Result.instance("旧的登录密码输入错误");
        }
    }
}
