package com.keyware.shandan.frame.aspect;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.keyware.shandan.common.entity.Result;
import com.keyware.shandan.common.util.*;
import com.keyware.shandan.frame.config.security.SecurityUtil;
import com.keyware.shandan.frame.properties.CustomProperties;
import com.keyware.shandan.system.entity.SysFormConfig;
import com.keyware.shandan.system.entity.SysRole;
import com.keyware.shandan.system.entity.SysUser;
import com.keyware.shandan.system.service.SysFormConfigService;
import com.keyware.shandan.system.service.SysRoleService;
import com.keyware.shandan.system.utils.SysSettingUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * AES + RSA 加解密AOP处理
 */
@Slf4j
@Aspect
@Component
public class SafetyAspect {
    private static final String[] PATHS = {"/business/metadata/save/all", "/search/metadata/condition/get", "/search/metadata/condition/query"};
    private static final List<String> EXCLUDE_PATHS = Arrays.asList(PATHS);
    @Autowired
    private CustomProperties customProperties;
    //jackson
    private final ObjectMapper mapper = new ObjectMapper();

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysFormConfigService formConfigService;

    /**
     * Pointcut 切入点
     */
    @Pointcut(value = "execution(public * com.keyware.shandan.*.controller.*.*(..))")
    public void safetyAspect() {
    }

    /**
     * 环绕通知
     */
    @Around(value = "safetyAspect()")
    public Object around(ProceedingJoinPoint joinPoint) {
        //jackson 序列化和反序列化 date处理
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        try {
            //判断api加密开关是否开启
            if (!SysSettingUtil.getCurrentSysSetting().getSysApiEncrypt()) {
                return joinPoint.proceed(joinPoint.getArgs());
            }

            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert attributes != null;
            //request对象
            HttpServletRequest request = attributes.getRequest();

            // 判断是否需要排除
            String path = request.getServletPath();
            if (EXCLUDE_PATHS.contains(path)) {
                return joinPoint.proceed(joinPoint.getArgs());
            }

            //前端公钥
            String publicKey = request.getParameter("publicKey");

            Object result = joinPoint.proceed(AspectUtil.parseJoinPointArgs(joinPoint, request));
            //执行方法之前解密，且只拦截post请求
            if ("post".equalsIgnoreCase(request.getMethod()) && !ServletFileUpload.isMultipartContent(request) && StringUtils.hasText(publicKey)) {
                // 返回之前对结果进行加密
                //每次响应之前随机获取AES的key，加密data数据
                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                String dataString = mapper.writeValueAsString(result);
                String key = AesUtil.getKey();
                String data = AesUtil.encrypt(dataString, key);

                //用前端的公钥来解密AES的key，并转成Base64
                String aesKey = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(key.getBytes(), publicKey));

                //转json字符串并转成Object对象，设置到Result中并赋值给返回值o
                return Result.of(mapper.readValue("{\"data\":\"" + data + "\",\"aesKey\":\"" + aesKey + "\"}", Object.class));
            } else {
                if (result instanceof ModelAndView) {
                    ModelAndView modelAndView = (ModelAndView) result;
                    modelAndView.addObject("publicKey", RsaUtil.getPublicKey());
                    modelAndView.addObject("sys", SysSettingUtil.getCurrentSysSetting());
                    SysUser loginUser = SecurityUtil.getLoginSysUser();
                    loginUser.setPassword(null);
                    modelAndView.addObject("loginUser", loginUser);
                    //登录用户角色信息
                    List<SysRole> userRoles = sysRoleService.getUserRoles(SecurityUtil.getLoginSysUser().getUserId());
                    modelAndView.addObject("userRoles", userRoles);
                    //当用户具有多个角色，只保留权限最大的角色
                    int index = 0;
                    if (userRoles.size() > 1) {
                        String roleId = "";
                        for (int i = 0; i < userRoles.size(); i++) {
                            if (StringUtils.isEmpty(roleId)) {
                                roleId = userRoles.get(i).getRoleId();
                                continue;
                            }
                            if (userRoles.get(i).getRoleId().compareTo(roleId) > 0) {
                                roleId = userRoles.get(i).getRoleId();
                                index = i;
                            }
                        }
                    }
                    //展示用户角色
                    modelAndView.addObject("userRole", userRoles.get(index));
                    List<SysFormConfig> formConfigList = formConfigService.list();

                    modelAndView.addObject("metadataFormConfig", formConfigList.stream().filter(conf -> conf.getFormType() == 1).collect(Collectors.toList()));
                    modelAndView.addObject("uploadFormConfig", formConfigList.stream().filter(conf -> conf.getFormType() == 2).collect(Collectors.toList()));
                    setStaticResourceAddress(request, modelAndView);
                }
                return result;
            }

        } catch (Throwable e) {
            //输出到日志文件中
            log.error(ErrorUtil.errorInfoToString(e));
            Class<?> clazz = AspectCommon.getMethodReturnType(joinPoint);
            if (clazz == String.class) {
                return e.getMessage();
            } else if (clazz == Result.class) {
                return Result.of(null, false, e.getMessage());
            } else {
                return null;
            }
        }
    }

    private void setStaticResourceAddress(HttpServletRequest request, ModelAndView mav) {
        // 前端公共资源文件数量
        int fileCount = 9;
        String address = customProperties.getStaticResourceAddress();
        if (StringUtils.isBlank(address)) {
            address = request.getContextPath();
        }

        // 定义一个与文件数量一样的数组
        String[] addressArray = new String[fileCount];
        String[] addrArr = address.split(",");
        int arrIndex = 0;
        for (int i = 0; i < fileCount; i++) {
            if (arrIndex >= addrArr.length) {
                arrIndex = 0;
            }
            addressArray[i] = addrArr[arrIndex];
            arrIndex++;
        }

        mav.addObject("staticResourceAddress1", addressArray[0]);
        mav.addObject("staticResourceAddress2", addressArray[1]);
        mav.addObject("staticResourceAddress3", addressArray[2]);
        mav.addObject("staticResourceAddress4", addressArray[3]);
        mav.addObject("staticResourceAddress5", addressArray[4]);
        mav.addObject("staticResourceAddress6", addressArray[5]);
        mav.addObject("staticResourceAddress7", addressArray[6]);
        mav.addObject("staticResourceAddress8", addressArray[7]);
        mav.addObject("staticResourceAddress9", addressArray[8]);
        mav.addObject("V", Math.random());

    }
}
