package com.patzn.lims.sys.controller;

import com.baomidou.kisso.SSOConfig;
import com.baomidou.kisso.SSOHelper;
import com.baomidou.kisso.common.CookieHelper;
import com.baomidou.kisso.common.IpHelper;
import com.baomidou.kisso.common.encrypt.TOTP;
import com.baomidou.kisso.common.util.RandomUtil;
import com.baomidou.kisso.security.token.SSOToken;
import com.patzn.lims.core.PtConstants;
import com.patzn.lims.core.api.PtAssert;
import com.patzn.lims.core.api.PtResult;
import com.patzn.lims.core.toolkit.RegexUtils;
import com.patzn.lims.core.web.LoginHelper;
import com.patzn.lims.core.web.SuperController;
import com.patzn.lims.sys.dto.UserRegDTO;
import com.patzn.lims.sys.entity.SysUser;
import com.patzn.lims.sys.service.ISysCompanyService;
import com.patzn.lims.sys.service.ISysUserPlatformService;
import com.patzn.lims.sys.service.ISysUserService;
import io.jsonwebtoken.Jwts;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.support.RequestContextUtils;

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

/**
 * <p>
 * 登录相关操作
 * </p>
 *
 * @author hubin
 * @since 2017-08-08
 */
@Slf4j
@RestController
@RequestMapping("/v1/sso")
public class SSOController extends SuperController {
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysUserPlatformService sysUserPlatformService;
    @Autowired
    private ISysCompanyService sysCompanyService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private MessageSource messageSource;

    @ApiOperation(value = "多语言切换", notes = "多语言切换 ")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "lang", value = "语言编码", paramType = "path", dataType = "String"),
    })
    @GetMapping("/language-{lang}")
    public PtResult<Boolean> language(@PathVariable("lang") String lang) {
        if (log.isDebugEnabled()) {
            Locale locale = request.getLocale();
            log.error("lang:{} , locale:{}", lang, locale.toString());
        }
        LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
        if (PtConstants.LANG_US.equals(lang)) {
            localeResolver.setLocale(request, response, Locale.US);
        } else {
            localeResolver.setLocale(request, response, Locale.CHINA);
        }
        return success(true);
    }


    @ApiOperation(value = "AJAX 登录", notes = "AJAX 登录后台")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名或者手机号码", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "password", value = "登录密码", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "newPwd", value = "首次登录激活的新密码", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "code", value = "手机验证码", paramType = "query", dataType = "String"),
    })
    @PostMapping("/ajax-login-back")
    public PtResult<Map<String, Object>> ajaxLoginBack(@RequestParam("username") String username, String password,
                                                String newPwd, String code) {
        boolean inactivate = false;
        if (StringUtils.isNotEmpty(newPwd)) {
            PtAssert.fail(newPwd.equals(password), "新旧密码不能一致");
            inactivate = true;
        }
        // 处理登录逻辑
        SysUser user;
        if (RegexUtils.isMobile(username) && StringUtils.isNotEmpty(code)) {
            user = sysUserService.loginByCode(username, code);
        } else {
            // 普通登录
            PtAssert.fail(null == password, "not-found-password", messageSource);
            user = sysUserService.login(username, password, true);
        }
        PtAssert.fail(null == user, "login-failed", messageSource);
        if (inactivate) {
            sysUserService.activation(user, newPwd);
        } else {
            // 未激活状态
            PtAssert.fail(2 == user.getStatus(), "inactivate");
            // 二次登录验证
            if (1 == user.getTwofactor()) {
                Map<String, Object> map = new HashMap<>(2);
                String ticket = UUID.randomUUID().toString();
                redisTemplate.opsForValue().set(ticket, String.valueOf(user.getId()));
                map.put("ticket", ticket);
                return success(map);
            }
        }

        // 设置登录 COOKIE
        return success(loginMap(user));
    }

    private Map<String, Object> loginMap(SysUser user) {
        Map<String, Object> map = new HashMap<>(6);
        setSSOCookie(user);
        map.put("otp", StringUtils.isNotEmpty(user.getTotp()));
        map.put("avatar", user.getAvatar());
        map.put("username", user.getUsername());
        map.put("admin", user.getAdmin());
        // 激活邮箱
        if (user.getVerifyEmail() == 1) {
            map.put("email", user.getEmail());
        }
        // 绑定手机
        if (user.getVerifyMobile() == 1) {
            map.put("mobile", user.getMobile());
        }
        return map;
    }

    @ApiOperation(value = "OTP 登录后台", notes = "AJAX OTP 登录后台")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ticket", value = "票据", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "code", value = "二次验证码", required = true, paramType = "query", dataType = "String"),
    })
    @PostMapping("/otp-login-back")
    public PtResult<Map<String, Object>> otpLoginBack(@RequestParam String ticket, @RequestParam String code) {
        PtAssert.fail(!RegexUtils.isNumber(code) || code.length() != 6, "OTP 验证码不合法");
        String userId = redisTemplate.opsForValue().get(ticket);
        PtAssert.fail(StringUtils.isEmpty(userId), "二次登录票据失效");
        SysUser user = sysUserService.getById(Long.valueOf(userId));
        PtAssert.fail(null == user, "二次登录用户不存在请联系管理员");
        PtAssert.fail(!TOTP.isValidCode(user.getTotp(), Long.valueOf(code)), "OTP 验证失败");
        return success(loginMap(user));
    }

    @ApiOperation(value = "AJAX 登录", notes = "AJAX 登录前台")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名或者手机号码", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "password", value = "登录密码", paramType = "query", dataType = "String"),
    })
    @PostMapping("/ajax-login-web")
    public PtResult<Map<String, Object>> ajaxLoginWeb(@RequestParam("username") String username, @RequestParam("password") String password) {
        Map<String, Object> map = null;
        SysUser user = sysUserService.login(username, password, false);
        if (null != user) {
            SSOHelper.setCookie(request, response, SSOToken.create(Jwts.builder())
                    .setId(user.getId()).setIssuer(user.getUsername())
                    .setUserAgent(request), false);
            map = new HashMap<>(6);
            map.put("avatar", user.getAvatar());
            map.put("username", user.getUsername());
            // 激活邮箱
            if (user.getVerifyEmail() == 1) {
                map.put("email", user.getEmail());
            }
            // 绑定手机
            if (user.getVerifyMobile() == 1) {
                map.put("mobile", user.getMobile());
            }
        }
        return success(map);
    }

    @ApiOperation(value = "APP 登录", notes = "APP 登录后台")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名或者手机号码", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "password", value = "登录密码", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "newPwd", value = "首次登录激活的新密码", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "code", value = "手机验证码", paramType = "query", dataType = "String"),
    })
    @PostMapping("/app-login")
    public PtResult<String> appLogin(@RequestParam("username") String username, String password,
                              String newPwd, String code) {
        return success(sysUserService.loginToken(request, username, password, newPwd, code));
    }

    @ApiOperation(value = "用户注册", notes = "普通用户注册")
    @PostMapping("/register")
    public PtResult<Boolean> register(@Valid UserRegDTO dto) {
        return success(sysUserService.register(dto, IpHelper.getIpAddr(request)));
    }

    /**
     * <p>
     * 设置登录 Cookie
     * </p>
     *
     * @param sysUser 用户对象
     */
    private boolean setSSOCookie(SysUser sysUser) {
        if (null != sysUser && null != sysUser.getId()) {
            String username = sysUser.getUsername();
            if (StringUtils.isNotEmpty(sysUser.getRealname())) {
                username = sysUser.getRealname();
            }
            SSOHelper.setCookie(request, response, SSOToken.create(Jwts.builder()
                    .claim(LoginHelper.COMPANY_ID, sysUser.getCompanyId())
                    .claim(LoginHelper.ADMIN, sysUser.getAdmin()))
                    .setId(sysUser.getId()).setIssuer(username)
                    .setUserAgent(request), false);
            // 添加一个 JS 可读标示
            SSOConfig config = SSOConfig.getInstance();
            CookieHelper.addCookie(response, config.getCookieDomain(), config.getCookiePath(),
                    "ln", RandomUtil.getCharacterAndNumber(6), -1, false, false);
            return true;
        }
        return false;
    }


    /**
     * <p>
     * AJAX 退出登录
     * </p>
     */
    @ResponseBody
    @GetMapping("/logout")
    public PtResult<Boolean> logout() {
        SSOHelper.clearLogin(request, response);
        SSOConfig config = SSOConfig.getInstance();
        CookieHelper.clearCookieByName(request, response, "ln",
                config.getCookieDomain(), config.getCookiePath());
        return success(true);
    }


    @ApiOperation(value = "修改登录密码", notes = "手机验证码修改登录密码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "mobile", value = "手机号码", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "password", value = "登录密码", required = true, paramType = "query", dataType = "String"),
    })
    @PostMapping("/change-password")
    public PtResult<Boolean> changePassword(@RequestParam("mobile") String mobile,
                                              @RequestParam("code") String code,
                                              @RequestParam("password") String password) {
        return success(sysUserService.changePassword(mobile, code, password));
    }


    @ApiOperation(value = "验证登录密码", notes = "锁屏验证登录密码是否正确")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "password", value = "登录密码", required = true, paramType = "query", dataType = "String"),
    })
    @PostMapping("/valid-password")
    public PtResult<Boolean> validPassword(@RequestParam String password) {
        return success(sysUserService.validPassword(this.currentUserId(), password));
    }
}
