package com.zfire.mall.manager.zfire.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.BooleanUtils;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zfire.mall.manager.annotation.ZfireField;
import com.zfire.mall.manager.bean.ExcelData;
import com.zfire.mall.manager.plus.entity.AdminField;
import com.zfire.mall.manager.utils.excel.ExcelUtils;
import com.zfire.mall.manager.zfire.bean.QueryParamBean;
import com.zfire.mall.manager.zfire.bean.ZfireParamBean;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.junit.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;


public class FieldUtils {


    @Test
    public void test() throws NoSuchFieldException, IllegalAccessException {

//        QueryParamBean map = new QueryParamBean();
//        map.setCompare("like");
//        map.setParam("name");
//        map.setValue("123");
//
//
//        QueryParamBean map2 = new QueryParamBean();
//        map2.setCompare("=");
//        map2.setParam("phone");
//        map2.setValue("1234");
//        //map2.setCreateTime(new Date());
//
//        Field[] declaredFields = map2.getClass().getDeclaredFields();
//
//        Field param = map2.getClass().getDeclaredField("param");
//        param.setAccessible(true);
//        Object o = param.get(map2);
//
//        Field createTime = map2.getClass().getDeclaredField("createTime");
//        createTime.setAccessible(true);
//        Object o2 = createTime.get(map2);
//        String typeName = createTime.getType().getTypeName();
//
//        String s = this.supplyParam(Arrays.asList(map, map2));
//        System.out.print(s);
    }


    /**
     * 组装最终的sql条件和排序
     * @param bean
     * @return
     */
    public static ZfireParamBean supplyParam(ZfireParamBean bean){
        //todo 框架自带防注入
        bean.setQuery(supplyParam(bean.getParams()));
        if(StringUtils.isNotEmpty(bean.getOrderBy()) && !bean.getOrderBy().contains("order by")) {
            bean.setOrderBy("order by " + bean.getOrderBy());
        }
        return bean;
    }

    /**
     *
     * 根据前端传的集合生成查询条件
     * [{
     *    "param":"条件名称",
     *    "compare":"比较符(><=like)"
     *    "value":"内容"
     * }]
     * @return
     */
    public static String supplyParam(List<QueryParamBean> params){
        StringBuffer sb = new StringBuffer(" where 1=1 ");
        if(params == null || params.size() == 0){
            return sb.toString();
        }

        for(QueryParamBean paramBean : params){
            if(paramBean.getValue() == null)
                continue;
            if(paramBean.getValue() instanceof  ArrayList){
                List<String> values = (List<String>) paramBean.getValue();
                if(values != null && values.size() > 0) {
                    String join = "";
                    for(String s : values){
                        s = replaceValue(s);
                        join += "'"+s+"',";
                    }
                    join = join.substring(0,join.length() - 1);
                    sb.append("and ").append(paramBean.getParam()).append(" in(").append(join).append(") ");
                }
            }else {
                String value = paramBean.getValue().toString();
                if(StringUtils.isEmpty(value))
                    continue;

                value = replaceValue(value);

                if (StringUtils.equals(paramBean.getCompare(), "like")) {
                    value = "%" + value + "%";
                }
                sb.append("and ").append(paramBean.getParam()).append(" ").append(paramBean.getCompare());
                if (value.equals("true") || value.equals("false")) {
                    sb.append(" ").append(value).append(" ");
                } else {
                    sb.append(" '").append(value).append("' ");
                }
            }
        }
        return sb.toString();
    }

    public static String replaceValue(String value){
        return value.replaceAll("'","").replaceAll("\"","")
                .replaceAll(" or ","").replaceAll(" union ","");
    }


    /**
     * 通用导出
     */
    public static void exportData(List datas, List<AdminField> titles, HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<List<Object>> rows = new ArrayList<>();
        List<String> excelTitles = new ArrayList<>();
        if(CollectionUtils.isEmpty(datas)){
//            throw new RemoteServiceException("暂无内容导出");
            excelTitles.add("暂无内容导出");
        } else {
//            Map<String, String> jMap = new LinkedHashMap<>();
            List<String> jList = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(titles)) {
//                for (AdminField title : titles) {
//                    jMap.put(title.getJName(), title.getLabel());
//                }
                jList = titles.stream().map(AdminField::getJName).collect(Collectors.toList());
            }
            if(jList.size() == 0){
                Object bean = datas.get(0);
                Field[] fields = bean.getClass().getDeclaredFields();
                for(Field field : fields) {
                    jList.add(field.getName());
                }
            }

            for (int i = 0; i < datas.size(); i++) {
                Object bean = datas.get(i);
                List<Object> row = new ArrayList<>();
                Field[] fields = bean.getClass().getDeclaredFields();
                List<Field> fieldList = Arrays.asList(fields);
                Map<String, Field> fieldMap = fieldList.stream().collect(Collectors.toMap(Field::getName, v -> v));

                for(String f:jList){
                //for (String key : jMap.keySet()) {
                    Field field = fieldMap.get(f);
                    field.setAccessible(true);
                    String jName = field.getName();
                    if (jList.size() > 0 && !jList.contains(jName)) {
                        continue;
                    }
                    String label = "";
                    ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
                    if(annotation == null)
                        continue;
                    label = annotation.value();

                    ZfireField zfireFieldAnnotation = field.getAnnotation(ZfireField.class);
                    if(zfireFieldAnnotation != null && zfireFieldAnnotation.hide())
                        continue;

                    //属性类型
                    String typeName = field.getType().getName();

                    Object value = field.get(bean);
                    //处理枚举类型
                    if (field.getType().isEnum()) {
                        for (Object enumO : field.getType().getEnumConstants()) {
                            Class<?> c = enumO.getClass();
                            Method getKey = c.getDeclaredMethod("getKey");
                            Method getValue = c.getDeclaredMethod("getValue");
                            Object invoke = getKey.invoke(enumO);
                            //String enumKey =  invoke+"";
                            String enumValue = (String) getValue.invoke(enumO);
                            if (value != null && value.toString().equals(enumO.toString())) {
                                value = enumValue;
                            }
                        }
                    }

                    if (value == null)
                        row.add("");

                    if (value != null) {
                        if ("java.util.Date".equals(typeName)) {
                            //todo 格式化时间
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            row.add(sdf.format(value));
                        } else {
                            row.add(value);
                        }
                    }
                    if (i == 0) {
                        excelTitles.add(label);
                    }
                //}
                }
                rows.add(row);
            }
        }
        ExcelData excelData = new ExcelData();
        excelData.setRows(rows);
        excelData.setTitles(excelTitles);
        //目前是异步导出，名字由前端根据菜单名定义
        ExcelUtils.exportExcel(request,response,"test.xlsx",excelData);
        //export(excelData,"test.xlsx");
    }




    public static void export(ExcelData excelData,String fileName) throws IOException {
        OutputStream outputStream = null;
        try {
            //标题
            List<List<String>> titles = new ArrayList<>();
            for(String title : excelData.getTitles()){
                titles.add(Arrays.asList(title));
            }

            //记录总数:实际中需要根据查询条件进行统计即可
            Integer totalCount = excelData.getRows().size();
            //每一个Sheet存放100w条数据
            Integer sheetDataRows = 1000000;
            //每次写入的数据量20w,每页查询20W
            Integer writeDataRows = 200000;
            //计算需要的Sheet数量
            Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
            Integer oneSheetWriteCount = sheetDataRows / writeDataRows;
            //计算最后一个sheet需要写入的次数
            Integer lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) : (totalCount / sheetDataRows / writeDataRows + 1));
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletResponse response = requestAttributes.getResponse();
            outputStream = response.getOutputStream();

            //样式
            WriteFont font = new WriteFont();
            font.setFontName("simsun");
            font.setFontHeightInPoints((short)10);
            font.setColor(IndexedColors.BLACK.index);
            WriteCellStyle cellStyle = new WriteCellStyle();
            cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
            cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            cellStyle.setWriteFont(font);
            cellStyle.setWrapped(false);
            cellStyle.setBorderTop(BorderStyle.THIN);
            cellStyle.setBorderLeft(BorderStyle.THIN);
            cellStyle.setBorderBottom(BorderStyle.THIN);
            cellStyle.setBorderRight(BorderStyle.THIN);
            //必须放到循环外，否则会刷新流
            ExcelWriter excelWriter = EasyExcel.write(outputStream).registerWriteHandler(new CellWriteHandler(){
                public void afterCellDispose(CellWriteHandlerContext context) {
                    CellWriteHandler.super.afterCellDispose(context);
                    if (BooleanUtils.isNotTrue(context.getHead())) {
                        context.getFirstCellData().setWriteCellStyle(cellStyle);
                    }
                }
            }).build();
            //开始分批查询分次写入
            for (int i = 0; i < sheetNum; i++) {
                //创建Sheet
//                WriteSheet sheet = new WriteSheet();
//                sheet.setSheetName("Sheet"+i);
//                sheet.setSheetNo(i);
                //循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
                for (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                    //分页查询一次20w
                    WriteSheet writeSheet = EasyExcel.writerSheet(i, "Sheet" + (i + 1)).head(titles)
                            .registerWriteHandler(new SimpleColumnWidthStyleStrategy(30))
                            .build();
                    //写数据
                    excelWriter.write(excelData.getRows(), writeSheet);
                }
            }
            // 下载EXCEL
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止浏览器端导出excel文件名中文乱码 当然和easyexcel没有关系
            //String fileName = URLEncoder.encode("员工信息", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            excelWriter.finish();
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (BeansException e) {
            e.printStackTrace();
        }finally {
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }


}
