/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package easydo.technology.system.security.rest;

import com.wf.captcha.base.Captcha;
import easydo.technology.annotation.rest.AnonymousGetMapping;
import easydo.technology.annotation.rest.AnonymousPostMapping;
import easydo.technology.components.JdbcClient;
import easydo.technology.config.RsaProperties;
import easydo.technology.exception.BadRequestException;
import easydo.technology.system.model.*;
import easydo.technology.system.security.config.bean.LoginCodeEnum;
import easydo.technology.system.security.config.bean.LoginProperties;
import easydo.technology.system.security.config.bean.SecurityProperties;
import easydo.technology.system.security.security.TokenProvider;
import easydo.technology.system.security.service.OnlineUserService;
import easydo.technology.system.security.service.dto.AuthUserDto;
import easydo.technology.system.security.service.dto.JwtUserDto;
import easydo.technology.utils.RedisUtils;
import easydo.technology.utils.RsaUtils;
import easydo.technology.utils.SecurityUtils;
import easydo.technology.utils.StringUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Zheng Jie
 * @date 2018-11-23
 *       授权、根据token获取用户详细信息
 */
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthorizationController {

    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final OnlineUserService onlineUserService;
    private final TokenProvider tokenProvider;
    private final JdbcClient jdbcClient;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    @Resource
    private LoginProperties loginProperties;

    @AnonymousPostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request)
            throws Exception {
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtil.isEmpty(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtil.isEmpty(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        SysUser user = jwtUserDto.getUser();

        // 1. 查询并校验租户 (对齐 Go 逻辑)
        if (StringUtil.isEmpty(user.getTenantId())) {
            throw new BadRequestException("用户未关联租户");
        }
        Tenant tenantQuery = new Tenant();
        tenantQuery.setId(user.getTenantId());
        Tenant tenant = jdbcClient.getJdbcModelById(tenantQuery);
        if (tenant == null) {
            throw new BadRequestException("企业不存在");
        }
        if (!"enable".equals(tenant.getStatus())) {
            throw new BadRequestException("企业已被禁用");
        }
        user.setTenant(tenant);

        // 2. 查询部门
        if (user.getDeptId() != null) {
            SysDept dept = new SysDept();
            dept.setId(user.getDeptId());
            user.setDept(jdbcClient.getJdbcModelById(dept));
        }

        Map<String, Object> paramMap = new HashMap<>();
        SysUsersRoles usersRoles = new SysUsersRoles();
        usersRoles.setUserId(user.getId());
        List<SysUsersRoles> rolesList = jdbcClient.getJdbcList(usersRoles);
        if (rolesList != null && !rolesList.isEmpty()) {
            List<Long> roleIdList = rolesList.stream().map(SysUsersRoles::getRoleId).collect(Collectors.toList());
            paramMap.put("idIn", roleIdList);
            List<SysRole> roleList = jdbcClient.getJdbcList(paramMap, SysRole.class);
            user.setRoleList(roleList);
        }
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token, request);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(3) {
            {
                put("token", properties.getTokenStartWith() + token);
                put("user", jwtUserDto);
                put("session_expires", System.currentTimeMillis() / 1000 + 7 * 24 * 3600);
            }
        };
        if (loginProperties.isSingleLogin()) {
            // 踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
        }

        return ResponseEntity.ok(authInfo);
    }

    @GetMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo() {
        return ResponseEntity.ok(SecurityUtils.getCurrentUser());
    }

    @AnonymousGetMapping(value = "/code")
    public ResponseEntity<Object> getCode() {
        // 获取运算的结果
        Captcha captcha = loginProperties.getCaptcha();
        String uuid = properties.getCodeKey() + UUID.randomUUID().toString();
        // 当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 保存
        redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {
            {
                put("img", captcha.toBase64());
                put("uuid", uuid);
            }
        };
        return ResponseEntity.ok(imgResult);
    }

}
