package cn.wisenergy.chnmuseum.party.web.controller;

import cn.wisenergy.chnmuseum.party.auth.SHA256PasswordEncryptionService;
import cn.wisenergy.chnmuseum.party.auth.util.JwtTokenUtil;
import cn.wisenergy.chnmuseum.party.common.checkcode.SpecCaptcha;
import cn.wisenergy.chnmuseum.party.common.enums.AuditOperationEnum;
import cn.wisenergy.chnmuseum.party.common.enums.AuditStatusEnum;
import cn.wisenergy.chnmuseum.party.common.log.MethodLog;
import cn.wisenergy.chnmuseum.party.common.log.OperModule;
import cn.wisenergy.chnmuseum.party.common.log.OperType;
import cn.wisenergy.chnmuseum.party.model.Audit;
import cn.wisenergy.chnmuseum.party.model.Role;
import cn.wisenergy.chnmuseum.party.model.TUser;
import cn.wisenergy.chnmuseum.party.model.Menu;
import cn.wisenergy.chnmuseum.party.service.RoleService;
import cn.wisenergy.chnmuseum.party.service.impl.TUserServiceImpl;
import cn.wisenergy.chnmuseum.party.service.impl.MenuServiceImpl;
import cn.wisenergy.chnmuseum.party.web.controller.base.BaseController;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * shiro权限控制登录Controller
 */
@Api(tags = "登录接口")
@RestController
public class LoginController extends BaseController {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private TUserServiceImpl userService;

    @Resource
    private RoleService roleService;

    @Resource
    private MenuServiceImpl menuService;

    @Resource
    private SysLogController sysLogController;

    private static final String SHIRO_JWT_TOKEN = "shiro:jwt:token:";
    //用户登录次数计数  redisKey 前缀
    private String SHIRO_LOGIN_COUNT = "shiro_login_count_";
    //用户登录是否被锁定    一小时 redisKey 前缀
    private String SHIRO_IS_LOCK = "shiro_is_lock_";

    // 未授权跳转的页面
    @RequestMapping(value = "403", method = RequestMethod.GET)
    public void noPermissions(HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(403);
        response.getWriter().write("{\"status\":403,\"message\":\"Unauthorized!\"}");
    }

    /**
     * 管理员ajax登录请求 后端用户登录
     *
     * @param username
     * @param password
     * @return
     */
    @RequestMapping(value = "ajaxLogin", method = RequestMethod.POST)
    public ResponseEntity<Map<String, Object>> ajaxLogin(@RequestParam(value = "username", required = true) String username,
                                                         @RequestParam(value = "password", required = true) String password,
//                                                         @RequestParam(value = "captcha", required = true) String captcha,
                                                         HttpServletRequest request) {
        Map<String, Object> resultMap = new LinkedHashMap<>();
//        String captchaId = request.getHeader("CaptchaId");
//        if (StringUtils.isNotBlank(captcha)) {
//            if (StringUtils.isNotBlank(captchaId)) {
//                String uuidCap = stringRedisTemplate.opsForValue().get(captchaId);
//                if (StringUtils.isNotBlank(uuidCap)) {
//                    if (!uuidCap.trim().equalsIgnoreCase(captcha.trim())) {
//                        stringRedisTemplate.delete(captchaId);
//                        resultMap.put("status", 400);
//                        resultMap.put("message", "验证码不正确!");
//                        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resultMap);
//                    }
//                } else {
//                    stringRedisTemplate.delete(captchaId);
//                    resultMap.put("status", 400);
//                    resultMap.put("message", "验证码已失效,请刷新页面!");
//                    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resultMap);
//                }
//            } else {
//                //stringRedisTemplate.delete(captchaId);
//                resultMap.put("status", 400);
//                resultMap.put("message", "验证码获取失败!");
//                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resultMap);
//            }
//        } else {
//            stringRedisTemplate.delete(captchaId);
//            resultMap.put("status", 400);
//            resultMap.put("message", "验证码不能为空!");
//            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(resultMap);
//        }
//        stringRedisTemplate.delete(captchaId);

        TUser user;
        if (StringUtils.isNoneBlank(username)) {

            try {
            //访问一次,计数一次
            ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
            if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + username))) {
                resultMap.put("resultCode", "500");
                resultMap.put("message", "由于密码输入错误次数大于5次,12小时内帐号已禁止登录!请您联系相关管理人员。");
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultMap);
            }

            user = userService.selectByUsername(username);
            if (user == null) {
                resultMap.put("resultCode", "500");
                resultMap.put("message", "用户名或密码不正确!");
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultMap);
            }

            if (AuditOperationEnum.DISABLE.name().equals(user.getStatus())) {
                resultMap.put("resultCode", "500");
                resultMap.put("message", "此帐号已禁用,请联系管理员!");
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultMap);
            }

            if (user.getPermanent()!=null&&!user.getPermanent()) {
                if (user.getEffectiveDate().isAfter(LocalDate.now())||user.getExiredDate().isBefore(LocalDate.now())) {
                    resultMap.put("resultCode", "500");
                    resultMap.put("message", "此帐号已失效,请联系管理员!");
                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultMap);
                }
            }

                byte[] salt = user.getPasswordSalt();
                String s1 = new String(SHA256PasswordEncryptionService.createPasswordHash(password, salt));
                if (!new String(SHA256PasswordEncryptionService.createPasswordHash(password, salt)).equals(new String(user.getPasswordHash()))) {
                    opsForValue.increment(SHIRO_LOGIN_COUNT + username, 1);
                    //计数大于5时,设置用户被锁定一小时
                    String s = opsForValue.get(SHIRO_LOGIN_COUNT + username);
                    if (StringUtils.isNotBlank(s)) {
                        if (Integer.parseInt(s) >= 5) {
                            opsForValue.set(SHIRO_IS_LOCK + username, "LOCK");
                            stringRedisTemplate.expire(SHIRO_IS_LOCK + username, 12, TimeUnit.HOURS);
                        }
                    }
                    throw new IncorrectCredentialsException("用户名或密码不正确!");
                }
                List<Role> roles = roleService.selectRoleByUserId(user.getId());
                List<String> list1 = new ArrayList<>();
                //获取当前用户角色拥有菜单
                List<Menu> userMenuPerms = new ArrayList<>();
                if (roles!=null&&roles.get(0)!=null) {
                    roles.stream().forEach(r -> list1.add(r.getId()));
                    user.setRoleList(list1);
                    userMenuPerms = this.menuService.getUserMenuPerms(list1);
                }

                //登录时插入系统日志
                String operationContent = username + "登录本系统";
                if (user.getOrgName() != null) {
                    operationContent += ",机构" + user.getOrgName();
                }
                this.sysLogController.insertSysLog(operationContent,user);

                String token = JwtTokenUtil.sign(username, user.getId());
                // 将token信息存入Redis
                stringRedisTemplate.opsForValue().set(SHIRO_JWT_TOKEN + token, user.getId(), 240, TimeUnit.MINUTES);

                resultMap.put("user", user);
                resultMap.put("token", token);
                resultMap.put("menuList", userMenuPerms);
                resultMap.put("resultCode", "200");
                resultMap.put("message", "登录成功");
                return ResponseEntity.ok(resultMap);
            } catch (Exception e) {
                resultMap.put("resultCode", "500");
                resultMap.put("message", e.getMessage());
            }
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resultMap);
    }

    @RequestMapping(value = "logout", method = RequestMethod.GET)
    public ResponseEntity<JSONObject> logout() {
        String token = request.getHeader("Authorization");
        try {
            if (StringUtils.isNotBlank(token)) {
//                SecurityUtils.getSubject().logout();
                this.stringRedisTemplate.delete(SHIRO_JWT_TOKEN + token);
            }
            JSONObject resultMap = new JSONObject();
            resultMap.put("resultCode", "200");
            resultMap.put("message", "成功");
            resultMap.put("data", "");
            return ResponseEntity.ok(resultMap);
        } catch (Exception e) {
            LOGGER.error("注销错误!", e);
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }

    @RequestMapping(value = {"/verifyCode"}, method = {RequestMethod.GET})
    public void verifyCode(HttpServletRequest request, HttpServletResponse response, Integer width, Integer height) {
        String uuid = UUID.randomUUID().toString();
        try {
            SpecCaptcha iVerifyCodeGen;
            if ((width != null && width > 0) && (height != null && height > 0)) {
                iVerifyCodeGen = new SpecCaptcha(width, height);
            } else {
                iVerifyCodeGen = new SpecCaptcha();
            }
            response.addHeader("CaptchaId", uuid);
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0L);
            response.setContentType("image/jpeg");
            String out = iVerifyCodeGen.out(response.getOutputStream());
            this.stringRedisTemplate.opsForValue().set(uuid, out, 1800L);
        } catch (IOException e) {
            LOGGER.info("", e);
        }
    }

    @RequestMapping(value = {"/verifyCode1"}, method = {RequestMethod.GET}, produces = MediaType.IMAGE_JPEG_VALUE)
    public @ResponseBody
    byte[] verifyCode1(HttpServletRequest request, HttpServletResponse response, Integer width, Integer height) {
        String uuid = UUID.randomUUID().toString();
        ByteArrayOutputStream baos = null;
        try {
            SpecCaptcha iVerifyCodeGen;
            if ((width != null && width > 0) && (height != null && height > 0)) {
                iVerifyCodeGen = new SpecCaptcha(width, height);
            } else {
                iVerifyCodeGen = new SpecCaptcha();
            }
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0L);
            response.setHeader("Access-Control-Allow-Headers", "CaptchaId");
            response.setHeader("Access-Control-Expose-Headers", "CaptchaId");
            response.addHeader("CaptchaId", uuid);

            baos = new ByteArrayOutputStream();
            String out = iVerifyCodeGen.out(baos);
            this.stringRedisTemplate.opsForValue().set(uuid, out, 1800L);
            return baos.toByteArray();
        } catch (Exception e) {
            LOGGER.info("", e);
        } finally {
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

}