package com.huigou.uasp.bmp.common.easysearch.controller;

import com.huigou.uasp.bmp.common.easysearch.EasySearch;
import com.huigou.uasp.client.CommonController;
import com.huigou.util.SDO;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author yonghuan
 */
@Component
@Aspect
public class EasySearchDispatcher {

    private final Map<String, Object[]> easySearchCache = new ConcurrentHashMap<>(16);
    private final static Object[] EMPTY_EASY_SEARCH = new Object[0];
    private static Method getSDOMethod;

    static {
        try {
            getSDOMethod = CommonController.class.getDeclaredMethod("getSDO");
            getSDOMethod.setAccessible(true);
        } catch (NoSuchMethodException e) {
        }
    }

    @Around("execution(* com.huigou.uasp.bmp.common.easysearch.controller.EasySearchController.execute(..))")
    public String intercept(ProceedingJoinPoint pjp) throws Throwable {
        SDO sdo = (SDO) getSDOMethod.invoke(pjp.getTarget());
        String configType = sdo.getProperty("configType", String.class, "system");
        String queryName = sdo.getString("queryName");
        Object[] config = getConfig(configType, queryName);
        if (config != EMPTY_EASY_SEARCH) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Object bean = config[0];
            Method method = (Method) config[1];
            Object model = method.invoke(bean, sdo);
            HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
            request.setAttribute("EASY_SEARCH_DATA", model);
            request.getRequestDispatcher("/common/easySearch.jsp").forward(request, response);
            return null;
        }
        return (String) pjp.proceed();
    }

    public Object[] getConfig(String configType, String queryName) {
        String cacheKey = String.format("%s.%s", configType, queryName);
        Object[] config = easySearchCache.get(cacheKey);
        if (config != null) {
            return config;
        }
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 获取到Servlet WebApplicationContext（不是Root WebApplicationContext）
        WebApplicationContext wac = ((WebApplicationContext) request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE));
        for (String beanName : wac.getBeanDefinitionNames()) {
            Object bean = wac.getBean(beanName);
            for (Method method : ReflectionUtils.getAllDeclaredMethods(bean.getClass())) {
                EasySearch easySearch = method.getAnnotation(EasySearch.class);
                if (easySearch != null && easySearch.configType().equals(configType) && easySearch.queryName().equals(queryName)) {
                    config = new Object[]{bean, method};
                    break;
                }
            }
            if (config != null) {
                break;
            }
        }
        if (config == null) {
            config = EMPTY_EASY_SEARCH;
        }
        easySearchCache.put(cacheKey, config);
        return config;
    }

}
