package com.izouma.awesomeadmin.web;

import com.izouma.awesomeadmin.dao.SysAppTokenMapper;
import com.izouma.awesomeadmin.dto.Result;
import com.izouma.awesomeadmin.model.UserInfo;
import com.izouma.awesomeadmin.shiro.PhoneCodeToken;
import com.izouma.awesomeadmin.util.CookieUtil;
import com.izouma.awesomeadmin.util.PropertiesFileLoader;
import com.izouma.awesomeadmin.util.VerifyCodeUtils;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.*;

@Controller
@RequestMapping("/auth")
public class AuthenticationController {

    @Autowired
    private SysAppTokenMapper sysAppTokenMapper;

    @RequestMapping(value = "/image", method = RequestMethod.GET)
    @ResponseBody
    public Result image(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");

        //生成随机字串
        String verifyCode = VerifyCodeUtils.generateVerifyCode(4);
        //存入会话session
        HttpSession session = request.getSession(true);
        session.setAttribute("rand", verifyCode.toLowerCase());
        //生成图片
        int w = 200, h = 80;
        try {
            VerifyCodeUtils.outputImage(w, h, response.getOutputStream(), verifyCode);
            return new Result(true, verifyCode);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new Result(false, "验证图片生成失败");
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public ModelAndView login(@RequestParam("username") String username,
                              @RequestParam("password") String password,
                              @RequestParam(value = "remember", required = false, defaultValue = "false") boolean remember,
                              @RequestParam(value = "requireToken", required = false, defaultValue = "false") boolean requireToken,
                              HttpServletResponse response,
                              HttpServletRequest request) {
        ModelAndView result = new ModelAndView(new MappingJackson2JsonView());
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
        Map<String, Object> map = login(usernamePasswordToken, remember, requireToken, 3, request, response);
        result.addAllObjects(map);
        return result;
    }

    @RequestMapping(value = "/loginSms", method = RequestMethod.POST)
    @ResponseBody
    public ModelAndView loginBySms(@RequestParam("phone") String phone,
                                   @RequestParam("code") String code,
                                   @RequestParam("sessionId") String sessionId,
                                   @RequestParam(value = "remember", required = false, defaultValue = "false") boolean remember,
                                   @RequestParam(value = "requireToken", required = false, defaultValue = "false") boolean requireToken,
                                   HttpServletRequest request,
                                   HttpServletResponse response) {
        ModelAndView result = new ModelAndView(new MappingJackson2JsonView());
        PhoneCodeToken phoneCodeToken = new PhoneCodeToken(phone, code, sessionId);
        Map<String, Object> map = login(phoneCodeToken, remember, requireToken, 3, request, response);
        result.addAllObjects(map);
        return result;
    }

    @RequiresAuthentication
    @RequestMapping(value = "/logout", method = RequestMethod.POST)
    @ResponseBody
    public Result logout(String token) {
        if (StringUtils.isNotEmpty(token)) {
            sysAppTokenMapper.delToken(token);
        }
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return new Result(true, "已退出登录");
    }

    private Map<String, Object> login(AuthenticationToken authenticationToken, boolean remember, boolean requireToken,
                                      int expireDays, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(authenticationToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            map.put("success", false);
            map.put("error", e.getMessage());
            return map;
        }
        UserInfo user = (UserInfo) subject.getPrincipal();
        if (remember) {
            HttpSession session = request.getSession();
            CookieUtil.addCookie(response, "JSESSIONID", session.getId(), expireDays * 24 * 60 * 60);
        }
        if (requireToken) {
            String token = createToken(user, expireDays, false);
            map.put("token", token);
        }
        map.put("success", true);
        map.put("data", user);
        return map;
    }

    private String createToken(UserInfo userInfo, int expireDays, boolean kickOut) {
        if (kickOut) {
            sysAppTokenMapper.delUserToken(userInfo.getId());
        }
        SecretKey key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(PropertiesFileLoader.getProperties("jwtsecret").getBytes()));
        JwtBuilder jwt = Jwts.builder();
        jwt.setId(UUID.randomUUID().toString())
                .setIssuer("admin")
                .setIssuedAt(new Date())
                .setSubject(userInfo.getId().toString())
                .signWith(key);
        if (expireDays > 0) {
            Date date = new Date(System.currentTimeMillis() + (expireDays * 24 * 60 * 60 * 1000));
            jwt.setExpiration(date);
        }
        String token = jwt.compact();
        sysAppTokenMapper.saveToken(userInfo.getId(), token);
        return token;
    }
}
