package com.bootdo.system.controller;

import com.alibaba.fastjson.JSONObject;
import com.bootdo.common.config.BootdoConfig;
import com.bootdo.common.config.ThirdPartSsoConfig;
import com.bootdo.common.controller.BaseController;
import com.bootdo.common.service.DictService;
import com.bootdo.common.utils.MD5Utils;
import com.bootdo.common.utils.R;
import com.bootdo.common.utils.StringUtils;
import com.bootdo.common.utils.WebUtils;
import com.bootdo.system.domain.ProvinceCode;
import com.bootdo.system.domain.ThirdMsgLogDO;
import com.bootdo.system.domain.UserDO;
import com.bootdo.system.service.*;
import com.bootdo.system.vo.PushUserVO;
import com.bootdo.system.vo.TokenAndNameVO;
import com.bootdo.system.vo.UserInfoVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 第三方单点登录认证 控制层
 *
 * @author Mr.xiBel
 */
@Controller
@RequestMapping("/sso")
public class ThirdPartSsoController extends BaseController {
    /**
     * logger
     */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 初始化密码
     */
    private static final String INIT_PWD = "123456";

    /**
     * 字段名称 code
     */
    private static final String FIELD_CODE = "code";
    private static final String FIELD_DATA = "data";
    private static final String FIELD_USERNAME = "username";
    private static final String FIELD_USER_INFO = "user_info";
    private static final String FIELD_ACCESS_TOKEN = "access_token";

    @Resource
    private TokenCachService tokenCachService;

    @Autowired
    BootdoConfig bootdoConfig;

    /**
     * sso相关配置
     */
    @Autowired
    private ThirdPartSsoConfig thirdPartSsoConfig;

    /**
     * 第三方日志记录
     */
    @Resource
    private ThirdMsgLogDOService thirdMsgLogDOService;

    @Resource
    private UserService userService;

    @Autowired
    private DictService dictService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private ProvinceCodeService provinceCodeService;

    //    @Log("第三方单点登录认证用户推送")
    @PostMapping("/pushUser")
    @ResponseBody
    public R pushUser(HttpServletRequest request/*, @RequestBody PushUserVO pushUserVO*/) {
        String authorization = request.getHeader("Authorization");
        // 这tm 不标准的json串在大街上捡的吗?
        String bodyString = getBodyString(request);
        logger.info("into method: pushUser, Authorization: {}, bodyString: {}", authorization, bodyString);

        // 处理不标准json串 为标准的jsonString
        String jsonString = StringEscapeUtils.unescapeJava(bodyString.replace("\"[{", "[{").replace("}]\"", "}]"));
        logger.info("method: pushUser, finishString: {}", jsonString);

        PushUserVO pushUserVO = JSONObject.parseObject(jsonString, PushUserVO.class);

        try {
            // 转换主要用户信息并入库
            List<UserDO> userDOS = convertUserInformation(pushUserVO);

            // 更新入库
            if (CollectionUtils.isNotEmpty(userDOS)) {
                logger.info("update user listInfo: {}", userDOS);
                userService.batchUpdateThirdPartUser(userDOS);
            }

            return R.ok();
        } catch (Exception e) {
            logger.error("pushUser error: {}", e.getMessage());
            e.printStackTrace();
            return R.error();
        }
    }


    /**
     * 转换主要用户信息
     *
     * @param pushUserVO
     * @return
     */
    private List<UserDO> convertUserInformation(PushUserVO pushUserVO) {
        List<UserDO> userList = new ArrayList<>();
        if (Objects.nonNull(pushUserVO) && CollectionUtils.isNotEmpty(pushUserVO.getUserInfos())) {
            List<UserInfoVO> userInfos = pushUserVO.getUserInfos();
            for (UserInfoVO userInfo : userInfos) {
                UserDO userDO = new UserDO();
                userDO.setUsername(userInfo.getUsername());
                userDO.setName(userInfo.getRealName());
                userDO.setMobile(userInfo.getPhone());
                userDO.setEmail("123456@qq.com");
                if (Objects.nonNull(userInfo.getSex())) {
                    userDO.setSex(userInfo.getSex().longValue());
                }

                // 省份命名不规范 这里通过字典转换为本系统的
                if (Objects.nonNull(userInfo.getProvince()) && !"null".equals(userInfo.getProvince())) {
                    ProvinceCode provinceCode = provinceCodeService.selectByCode(Integer.parseInt(userInfo.getProvince()));
                    if (Objects.nonNull(provinceCode)) {
                        userDO.setProvince(provinceCode.getProvinceName());
                    }
                }
                if (Objects.nonNull(userInfo.getCity()) && !"null".equals(userInfo.getCity())) {
                    ProvinceCode provinceCode = provinceCodeService.selectByCode(Integer.parseInt(userInfo.getCity()));
                    if (Objects.nonNull(provinceCode)) {
                        userDO.setCity(provinceCode.getProvinceName());
                    }
                }
                if (Objects.nonNull(userInfo.getArea()) && !"null".equals(userInfo.getArea())) {
                    ProvinceCode provinceCode = provinceCodeService.selectByCode(Integer.parseInt(userInfo.getArea()));
                    if (Objects.nonNull(provinceCode)) {
                        userDO.setDistrict(provinceCode.getProvinceName());
                    }
                }

                userDO.setEmail(userInfo.getEmail());
                // 正常
                userDO.setStatus(1);
                // 部门 默认给19 工信部
                userDO.setDeptId(thirdPartSsoConfig.getPushDeptId() == null ? null : thirdPartSsoConfig.getPushDeptId());
                userDO.setLiveAddress(userInfo.getPostalAddress());

                // 这里 hobby字段用来表示 该条数据是否来自第三方
                userDO.setHobby("第三方");

                // 设置自定义密码
                userDO.setPassword(MD5Utils.encrypt(userInfo.getUsername(), INIT_PWD));

                userList.add(userDO);
            }
        }
        return userList;
    }


    /**
     * 接收code并认证跳转到首页
     *
     * @param code
     * @return
     */
    @GetMapping("/indexPage")
    public String indexPage(HttpServletRequest request, HttpServletResponse response, String code) throws IOException {
        logger.info("into method: indexPage, code: {}", code);
        if (StringUtils.isBlank(code)) {
            return "redirect:/login";
        }

        // 获取token
        Boolean directlyUsername = thirdPartSsoConfig.getDirectlyUsername();
        TokenAndNameVO tokenAndNameVO = getTokenOrUserName(code, directlyUsername);

        // 获取不到token 403
        if (Objects.isNull(tokenAndNameVO)) {
            logger.info("method: indexPage, tokenAndNameVO is null");
            return "error/403";
        }

        // 根据用户详情获取用户名(需要保证用户名称唯一)
        if (!directlyUsername) {
            String username = getUsernameByToken(tokenAndNameVO.getToken());
            tokenAndNameVO.setUsername(username);
        }


        // 开始登陆本系统 or 没有登录权限
        logger.info("method: indexPage, tokenAndNameVO: {}", tokenAndNameVO.toString());
        UserDO userDO = userService.getByHobbyAndUserName(tokenAndNameVO.getUsername());

        // 获取不到用户重定向到登陆
        if (Objects.isNull(userDO)) {
            logger.info("method: indexPage, userDO is null");
            return "error/403";
        }

        // 检查权限
        Set<String> perms = menuService.listPerms(userDO.getUserId());
        if (CollectionUtils.isEmpty(perms)) {
            return "error/403";
        }

        // 成功时 跳转到首页
        return loginSystem(userDO, tokenAndNameVO.getToken());
    }

    /**
     * 登陆系统
     *
     * @param userDO
     * @return
     */
    private String loginSystem(UserDO userDO, String token) {
        logger.info("method: loginSystem, user: {}, token: {}", userDO.getUserId(), token);
        UsernamePasswordToken token1 = new UsernamePasswordToken(userDO.getUsername(), userDO.getPassword());
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token1);
            // 登出通知
            tokenCachService.saveTokenCach(userDO.getUserId().toString(), token);
            logger.info("method: loginSystem, user: {}, token: {}, success!!!!!", userDO.getUserId(), token);
            return "redirect:/index";
        } catch (AuthenticationException e) {
            logger.info("method: loginSystem, user: {}, token: {}, 1-error !!!!!!", userDO.getUserId(), token);
            return "error/403";
        } catch (Exception e) {
            logger.info("method: loginSystem, user: {}, token: {}, 2-error !!!!!!", userDO.getUserId(), token);
            e.printStackTrace();
            return "error/500";
        }
    }

    /**
     * 根据token获取用户名
     *
     * @param token
     * @return
     * @throws IOException
     */
    private String getUsernameByToken(String token) throws IOException {
        Map<String, String> headerMap = new HashMap<>(10);
        headerMap.put("Authorization", "Bearer " + token);
        String userResult = WebUtils.doGet(thirdPartSsoConfig.getUserUrl(), null, "UTF-8", headerMap);
        logger.info("method: getUserDetailByToken, token: {}, url: {}, Result: {}", token, thirdPartSsoConfig.getUserUrl(), userResult);
        thirdMsgLogDOService.insert(new ThirdMsgLogDO("第三方系统", new Date(), "根据token获取用户详情", userResult));

        if (StringUtils.isNotBlank(userResult)) {
            JSONObject userResultJsonObject = JSONObject.parseObject(userResult);
            // code 状态 为0时正常
            if (userResultJsonObject.containsKey(FIELD_CODE) && userResultJsonObject.getInteger(FIELD_CODE).equals(0)) {
                // 获取用户data数据
                JSONObject dataJsonObject = userResultJsonObject.getJSONObject(FIELD_DATA);
                // data 中获取用户信息  因其第三方sso系统不完善, 所以具体的权限资源列表使用当前系统的权限资源列表
                return dataJsonObject.getString(FIELD_USERNAME);
            }
        }

        return null;
    }

    /**
     * 根据code获取用户token或者用户名称
     *
     * @param code
     * @param isUserName
     * @return
     * @throws IOException
     */
    private TokenAndNameVO getTokenOrUserName(String code, boolean isUserName) throws IOException {
        TokenAndNameVO tokenAndNameVO = new TokenAndNameVO();
        String tokenResult = WebUtils.doGet(thirdPartSsoConfig.getTokenUrl(code), null);
        logger.info("method: getToken, code: {}, url: {}, Result: {}", code, thirdPartSsoConfig.getTokenUrl(code), tokenResult);
        thirdMsgLogDOService.insert(new ThirdMsgLogDO("第三方系统", new Date(), "根据code获取用户token", tokenResult));

        if (StringUtils.isNotBlank(tokenResult)) {
            JSONObject tokenResultJsonObject = JSONObject.parseObject(tokenResult);
            if (tokenResultJsonObject.containsKey(FIELD_CODE)) {
                return null;
            }

            // 获取用户名
            if (isUserName) {
                JSONObject userInfo = tokenResultJsonObject.getJSONObject(FIELD_USER_INFO);
                tokenAndNameVO.setUsername(userInfo.getString(FIELD_USERNAME));
            }

            // 获取用户token
            tokenAndNameVO.setToken(tokenResultJsonObject.getString(FIELD_ACCESS_TOKEN));
            return tokenAndNameVO;
        }

        return null;
    }

    /**
     * 获取请求Body
     *
     * @param request request
     * @return String
     */
    public String getBodyString(final ServletRequest request) {
        try {
            return inputStream2String(request.getInputStream());
        } catch (IOException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 存储body数据的容器
     */
    private byte[] body;

    /**
     * 获取请求Body
     *
     * @return String
     */
    public String getBodyString() {
        final InputStream inputStream = new ByteArrayInputStream(body);

        return inputStream2String(inputStream);
    }

    /**
     * 将inputStream里的数据读取出来并转换成字符串
     *
     * @param inputStream inputStream
     * @return String
     */
    private String inputStream2String(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            logger.error("", e);
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.error("", e);
                }
            }
        }

        return sb.toString();
    }
}
