package com.brframework.commoncms.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.brframework.commoncms.annatotion.option.CmsOption;
import com.brframework.commoncms.core.DefPanelOption;
import com.brframework.commoncms.core.Option;
import com.brframework.commoncms.core.Panel;
import com.brframework.commoncms.core.PanelOption;
import com.brframework.commoncms.core.layout.PanelLayout;
import com.brframework.commoncms.core.layout.PanelLayoutColumn;
import com.brframework.commoncms.core.option.DefOption;
import com.brframework.commoncms.utils.ControllerUtil;
import com.brframework.commoncms.utils.excel.ExcelUtil;
import com.brframework.commonweb.exception.HandleException;
import com.brframework.commonweb.json.JSONResult;
import com.brframework.commonweb.json.Page;
import com.brframework.commonweb.json.PageParam;
import com.brframework.commonweb.utils.RequestResponseUtils;
import com.brframework.commonweb.utils.ServletUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * 界面过滤器
 *
 * @author xu
 * @date 2018-10-12 01:17:38
 */
@Component
@WebFilter(urlPatterns = "/*", filterName = "panelFilter")
@Order(0)
@Slf4j
public class PanelFilter implements Filter {

    @Autowired
    RequestMappingHandlerMapping requestMappingHandlerMapping;
    @Autowired
    RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String view = ((HttpServletRequest) request).getHeader("View");
        String export = ((HttpServletRequest) request).getHeader("Export");
        if ("admin".equals(view)) {
            handlerView(request, response, chain);
        } else if ("excel".equals(export)){
            handlerExportExcel(request, response, chain);
        } else {
            //非指定情况下返回正常数据
            chain.doFilter(request, response);
        }

    }

    @Override
    public void destroy() {

    }

    /**
     * 页面配置
     * @param request
     * @param response
     * @param chain
     */
    private void handlerView(ServletRequest request, ServletResponse response, FilterChain chain){
        try {
            HandlerExecutionChain handler = requestMappingHandlerMapping.getHandler(ServletUtils.request());
            if(handler == null){
                throw new HandleException("资源不存在，请检查" + ServletUtils.request().getRequestURI());
            }
            HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler();

            //生成Panel
            Panel managerPanel = Panel.builder()
                    .layout(createLayout(handlerMethod.getMethod()))
                    .option(createOption(handlerMethod.getMethod()))
                    .build();

            RequestResponseUtils.writerJson(response, JSONResult.ok(managerPanel).toString());
        } catch (Throwable e) {
            log.error("出现错误", e);
        }
    }

    /**
     * 文件导出
     * @param request
     * @param response
     * @param chain
     */
    private void handlerExportExcel(ServletRequest request, ServletResponse response, FilterChain chain){
        try {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;

            HandlerExecutionChain handler = requestMappingHandlerMapping.getHandler(ServletUtils.request());
            if(handler == null){
                throw new HandleException("资源不存在，请检查" + ServletUtils.request().getRequestURI());
            }
            HandlerMethod handlerMethod = (HandlerMethod) handler.getHandler();

            //使用Spring MVC 接口调用参数处理模块实现模拟的Controller调用
            ModelAndViewContainer container =new ModelAndViewContainer();
            ServletWebRequest webRequest = new ServletWebRequest(httpServletRequest, httpServletResponse);
            InvocableHandlerMethod invocableHandlerMethod = new InvocableHandlerMethod(handlerMethod);
            HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
            composite.addResolvers(requestMappingHandlerAdapter.getArgumentResolvers());
            invocableHandlerMethod.setHandlerMethodArgumentResolvers(composite);
            WebDataBinderFactory binderFactory = new ServletRequestDataBinderFactory(Lists.newArrayList(),
                    requestMappingHandlerAdapter.getWebBindingInitializer());
            invocableHandlerMethod.setDataBinderFactory(binderFactory);

            //生成新的分页参数
            PageParam pageParam = new PageParam();
            pageParam.setPageIndex(1);
            pageParam.setPageSize(50000);

            //调用数据
            Object o = invocableHandlerMethod.invokeForRequest(webRequest, container, pageParam);

            JSONResult<Page> result = (JSONResult<Page>) o;

            Map<String, String> headers = Maps.newLinkedHashMap();
            List<Map<String, String>> values = Lists.newArrayList();

            //得到excel的header
            PanelLayout layout = createLayout(handlerMethod.getMethod());
            for (PanelLayoutColumn column : layout.getColumns()) {
                if(column.isSelectShow()){
                    headers.put(column.getKey(), column.getTitle());
                }
            }

            //将数据转换成JsonArray，目的是解决导出数据里的时间序列化的问题
            String dataJson = JSON.toJSONString(result.getData().getList());
            JSONArray jsonArray = JSON.parseArray(dataJson);

            for (Object v : jsonArray) {

                Map<String, String> node = Maps.newLinkedHashMap();
                JSONObject jsonObject = (JSONObject) v;

                for (String key : headers.keySet()) {
                    node.put(key, jsonObject.getString(key));
                }
                values.add(node);
            }

            //开始到处excel文件
            //1.设置文件ContentType类型，这样设置，会自动判断下载文件类型
            httpServletResponse.setContentType("multipart/form-data");
            //2.设置文件头：最后一个参数是设置下载文件名
            httpServletResponse.setHeader("Content-Disposition", "attachment;fileName=export.xls");
            ExcelUtil.exportExcel(headers, values, response.getOutputStream());

        } catch (Throwable e) {
            log.error("出现错误", e);
        }
    }


    /**
     * 创建Option
     *
     * @return
     */
    private PanelOption createOption(Method method) {

        //获取Panel注解类型
        com.brframework.commoncms.annatotion.Panel panel = AnnotatedElementUtils.findMergedAnnotation(method, com.brframework.commoncms.annatotion.Panel.class);

        List<Option> optionList = Lists.newArrayList();
        for (CmsOption option : panel.options()) {
            if(ControllerUtil.hasMethodOptionRole(method, option)){
                Option defOption = new DefOption();
                defOption.create(method, option);
                optionList.add(defOption);
            }
        }

        return DefPanelOption.builder()
                .options(optionList)
                .build();
    }

    /**
     * 创建Group
     *
     * @return
     */
    private PanelLayout createLayout(Method method) throws Throwable {

        //获取Panel注解类型
        com.brframework.commoncms.annatotion.Panel panel = AnnotatedElementUtils.findMergedAnnotation(method, com.brframework.commoncms.annatotion.Panel.class);
        if (panel == null) {
            throw new HandleException("类" + method.getDeclaringClass().getName() + "方法" + method.getName() + "不存在Panel相关注解");
        }
        PanelLayout layout = panel.type().newInstance();

        layout.create(method);

        return layout;
    }
}
