package com.crm.rely.backend.aspect;

import com.crm.rely.backend.configuration.LocaleMessage;
import com.crm.rely.backend.configuration.LogQuartzComponent;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.core.constant.FeignClientAnnotation;
import com.crm.rely.backend.core.dto.base.BaseResultDto;
import com.crm.rely.backend.core.entity.base.BaseDateAndPageSearchEntity;
import com.crm.rely.backend.core.entity.base.DateBaseSearchEntity;
import com.crm.rely.backend.core.pojo.BaseTable;
import com.crm.rely.backend.exception.ControllerException;
import com.crm.rely.backend.service.RedisService;
import com.crm.rely.backend.util.DateUtil;
import com.google.common.base.Strings;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author houn
 */
@Aspect
@Component
@Order(1)
public class ControllerAop {

    @Autowired
    private LocaleMessage localeMessage;

    @Autowired
    private RedisService redisService;

    private ThreadLocal<Map<String, Map<Long, Map<LogType, Object>>>> threadLocal = new ThreadLocal<>();


    /**
     * 添加到 threadLocal中
     *
     * @param className
     * @param id
     * @param logType
     * @param object
     * @author: houn
     * @date: 2019/7/22 15:20
     */
    public void addThreadLocal(String className, Long id, LogType logType, Object object) {
        //获取ThreadLocal中的值
        Map<String, Map<Long, Map<LogType, Object>>> mapMapMap = threadLocal.get();
        if (mapMapMap == null) {
            mapMapMap = new HashMap<>(10);
        }
        //根据类获取数据
        Map<Long, Map<LogType, Object>> mapMap = mapMapMap.get(className);
        if (mapMap == null) {
            mapMap = new HashMap<>(10);
        }
        Iterable iterable = null;
        if (id == null && logType.equals(LogType.DELETE)) {
            if (object instanceof Long) {
                id = (Long) object;
                object = null;
            } else if (object instanceof Iterable) {
                iterable = (Iterable<Long>) object;
            }
        }
        if (iterable != null) {
            LogType finalLogType = logType;
            Map<Long, Map<LogType, Object>> finalMapMap = mapMap;
            iterable.forEach(value -> {
                if (value instanceof Long) {
                    Long idIterable = (Long) value;
                    Map<LogType, Object> map = getMap(finalMapMap, idIterable);
                    map.put(finalLogType, null);
                    finalMapMap.put(idIterable, map);
                } else if (value instanceof BaseTable) {
                    BaseTable tableIterable = (BaseTable) value;
                    Map<LogType, Object> map = getMap(finalMapMap, tableIterable.getId());
                    map.put(finalLogType, null);
                    finalMapMap.put(tableIterable.getId(), map);
                }
            });
        } else {
            //根据类名 在根据id获取操作类型
            Map<LogType, Object> map = getMap(mapMap, id);
            //如果是更新 看一下是否存在LogType.INSERT 如果存在LogType.INSERT对应的数据 将logType改为LogType.INSERT
            if (logType.equals(LogType.UPDATE)) {
                Object mapObject = map.get(LogType.INSERT);
                if (mapObject != null) {
                    logType = LogType.INSERT;
                }
            }
            //根据操作类型添加数据
            map.put(logType, object);
            mapMap.put(id, map);
        }

        mapMapMap.put(className, mapMap);
        threadLocal.set(mapMapMap);
    }

    private Map<LogType, Object> getMap(Map<Long, Map<LogType, Object>> mapMap, Long id) {
        Map<LogType, Object> map = mapMap.get(id);
        if (map == null) {
            map = new HashMap<>(4);
        }
        return map;
    }

    @Pointcut("execution(* com.*.*.controller.*.*(..))")
    public void allController() {
    }

    @Pointcut("execution(* com.*.*.controller.*.*.*(..))")
    public void allIbController() {
    }

    @Before(value = "allController()||allIbController()")
    public void allControllerBefore(JoinPoint joinPoint) {
        for (Object arg : joinPoint.getArgs()) {
            if (arg instanceof Iterable) {
                Iterable iterable = (Iterable) arg;
                iterable.forEach(value -> {
                    setInfo(value);
                });
            } else {
                setInfo(arg);
            }
        }
        clear();
    }

    private void setInfo(Object arg) {
        if (arg instanceof BaseDateAndPageSearchEntity) {
            BaseDateAndPageSearchEntity dateAndPageSearchEntity = (BaseDateAndPageSearchEntity) arg;
            if (dateAndPageSearchEntity != null && dateAndPageSearchEntity.getTemporaryDate() == null) {
                if (dateAndPageSearchEntity.getEndDate() != null) {
                    dateAndPageSearchEntity.setTemporaryDate(dateAndPageSearchEntity.getEndDate());
                    if (dateAndPageSearchEntity.getEndDate() != null) {
                        Date endDate = DateUtil.operationDay(dateAndPageSearchEntity.getEndDate(), 1);
                        dateAndPageSearchEntity.setEndDate(DateUtil.parseDate(endDate));
                    }
                }
                if (dateAndPageSearchEntity.getStartDate() != null) {
                    dateAndPageSearchEntity.setStartDate(DateUtil.parseDate(dateAndPageSearchEntity.getStartDate()));
                }
            }
        } else if (arg instanceof DateBaseSearchEntity) {
            DateBaseSearchEntity dateBaseSearchEntity = (DateBaseSearchEntity) arg;
            if (dateBaseSearchEntity != null && dateBaseSearchEntity.getTemporaryDate() == null) {
                if (dateBaseSearchEntity.getEndDate() != null) {
                    dateBaseSearchEntity.setTemporaryDate(dateBaseSearchEntity.getEndDate());
//                    if (dateBaseSearchEntity.getEndDate() != null) {
//                        Date endDate = DateUtil.operationDay(dateBaseSearchEntity.getEndDate(), 1);
//                        dateBaseSearchEntity.setEndDate(DateUtil.parseDate(endDate));
//                    }
                    if (dateBaseSearchEntity.getEndDate() == null) {
                        dateBaseSearchEntity.setEndDate(DateUtil.getMonthEnd(dateBaseSearchEntity.getEndDate()));
                    } else {
                        dateBaseSearchEntity.setEndDate(DateUtil.addDate(dateBaseSearchEntity.getEndDate()));
                    }
                }
                if (dateBaseSearchEntity.getStartDate() != null) {
                    dateBaseSearchEntity.setStartDate(DateUtil.parseDate(dateBaseSearchEntity.getStartDate()));
                }
            }
        }
    }

    @AfterReturning(value = "allController()||allIbController()", returning = "resultObject")
    public void allControllerAfterReturning(Object resultObject) {

        if (resultObject instanceof BaseResultDto) {
            BaseResultDto baseResultDto = (BaseResultDto) resultObject;

            baseResultDto.setMsg(localeMessage.getMessage(baseResultDto.getMsg(), baseResultDto.getMsg()));
            if (baseResultDto.getCode() != Constants.SUCCESS_CODE) {
                clear();
                return;
            }
        }

        addQueue();
    }


    /**
     * @description: 添加到队列中
     * @param: 无参数
     * @return: 无返回值
     * @author: houn
     * @date: 2019/7/22 15:18
     */
    private void addQueue() {
//        String url="";
//        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        if (null != attributes) {
//            HttpServletRequest request = attributes.getRequest();
//           String url= request.getRequestURI();
//        }

        Map<String, Map<Long, Map<LogType, Object>>> mapMapMap = threadLocal.get();
        if (mapMapMap == null) {
            clear();
            return;
        }

        clear();
//        LogQuartzComponent.add(mapMapMap,url);
        LogQuartzComponent.add(mapMapMap);
    }

    public void clear() {
        threadLocal.remove();
    }

    /**
     * feign调用Token验证
     *
     * @param joinPoint
     * @param feignClientAnnotation
     */
    @Before(value = "@annotation(feignClientAnnotation)")
    public void before(JoinPoint joinPoint, FeignClientAnnotation feignClientAnnotation) throws Exception {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String feignToken = request.getHeader(Constants.Feign_Token);
        String accessToken = request.getHeader(Constants.ACCESS_TOKEN);

        if (Strings.isNullOrEmpty(feignToken) || Strings.isNullOrEmpty(accessToken)) {
            throw new ControllerException(Constants.NOT_PERMIT);
        }

        String feignTokenValue = redisService.getObject(feignToken);

        if (Strings.isNullOrEmpty(feignTokenValue) || !accessToken.equals(feignTokenValue)) {
            throw new ControllerException(Constants.NOT_PERMIT);
        } else {
            redisService.remove(feignToken);
        }
    }


}
