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

import cn.hutool.core.date.LocalDateTimeUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
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.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.gree.mall.manager.annotation.ZfireField;
import com.gree.mall.manager.bean.ExcelData;
import com.gree.mall.manager.bean.admin.AdminUserCom;
import com.gree.mall.manager.constant.Constant;
import com.gree.mall.manager.plus.entity.AdminField;
import com.gree.mall.manager.zfire.bean.QueryParamBean;
import com.gree.mall.manager.zfire.bean.ZfireParamBean;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.jetbrains.annotations.NotNull;
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.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
public class FieldUtils {


    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) {
        return supplyParam(bean, null);
    }

    public static ZfireParamBean supplyParam(ZfireParamBean bean, Class cls) {
        //限制最多查询10w条
        if (bean.getPageSize() != null && (bean.getPageSize().equals(-1) || bean.getPageSize() > Constant.PAGE_SIZE)) {
            bean.setPageSize(Constant.PAGE_SIZE);
        }
        //todo 框架自带防注入
        bean.setQuery(supplyParam(bean.getParams()));
        if (StringUtils.isNotBlank(bean.getCompanyWechatId())) {
            bean.setQuery(bean.getQuery() + " and a.company_wechat_id = '" + bean.getCompanyWechatId() + "'");
        }
        if (bean.getClazzType() != null) {
            bean.setSelected(buildSelectColumn(bean.getClazzType()));
        } else if (cls != null) {
            bean.setSelected(buildSelectColumn(cls));
        }
        if (StringUtils.isNotBlank(bean.getOrderBy()) && !bean.getOrderBy().contains("order by")) {
            bean.setOrderBy("order by " + bean.getOrderBy());
        }
        return bean;
    }

    public static ZfireParamBean supplyParam(ZfireParamBean bean, Class cls, AdminUserCom adminUser) {
        if (Objects.nonNull(adminUser.getAdminCompanyWechat())) {
            bean.setCompanyWechatId(adminUser.getAdminCompanyWechat().getCompanyWechatId());
        }
        bean.setAdminWebsitIds(adminUser.getAdminWebsitIds());
        //限制最多查询10w条
        if (bean.getPageSize() != null && (bean.getPageSize().equals(-1) || bean.getPageSize() > Constant.PAGE_SIZE)) {
            bean.setPageSize(Constant.PAGE_SIZE);
        }
        //todo 框架自带防注入
        bean.setQuery(supplyParam(bean.getParams()));
        if (StringUtils.isNotBlank(bean.getCompanyWechatId())) {
            bean.setQuery(bean.getQuery() + " and a.company_wechat_id = '" + bean.getCompanyWechatId() + "'");
        }
        if (bean.getClazzType() != null) {
            bean.setSelected(buildSelectColumn(bean.getClazzType()));
        } else if (cls != null) {
            bean.setSelected(buildSelectColumn(cls));
        }
        if (StringUtils.isNotBlank(bean.getOrderBy()) && !bean.getOrderBy().contains("order by")) {
            bean.setOrderBy("order by " + bean.getOrderBy());
        }
        return bean;
    }


    public static ZfireParamBean materialParam(ZfireParamBean bean, Class cls, AdminUserCom adminUser) {
        return supplyParam(bean, cls, adminUser);
    }

    /**
     * 组装最终的sql查询字段
     *
     * @param clazz
     * @return
     */
    private static String buildSelectColumn(Class clazz) {
        if (Objects.isNull(clazz)) {
            return "*";
        }
        String defaultTbName = Optional.of(clazz)
                .filter(item -> item.isAnnotationPresent(ZfireField.class))
                .map(item -> item.getAnnotation(ZfireField.class))
                .map(ZfireField.class::cast)
                .map(ZfireField::tbName)
                .orElse("");

        Field[] fields = clazz.getDeclaredFields();
        List<String> sqlNameList = Lists.newArrayList();
        for (Field field : fields) {
            ZfireField annotation = field.getAnnotation(ZfireField.class);
            if (annotation != null && annotation.ignoreSelect()) {
                continue;
            }

            String fieldTbName = Optional.ofNullable(annotation)
                    .map(ZfireField::tbName)
                    .filter(StringUtils::isNotBlank)
                    .orElse(defaultTbName);
//            String sqlName = Optional.ofNullable(annotation)
//                    .map(ZfireField::colName)
//                    .filter(StringUtils::isNotBlank)
//                    .orElseGet(() -> parseLineColName(field.getName(), fieldTbName));
            String sqlName = parseLineColName(field.getName(), fieldTbName);
            sqlNameList.add(sqlName);
        }
        if (CollectionUtils.isEmpty(sqlNameList)) {
            return "*";
        }
        return Joiner.on(",")
                .skipNulls()
                .join(sqlNameList);
    }

    @NotNull
    private static String parseLineColName(String name, String fieldTbName) {
        String lineName = name.replaceAll("[A-Z]", "_" + "$0").toLowerCase();
        String sqlName = StringUtils.isNotBlank(fieldTbName) ? fieldTbName + "." + lineName : lineName;
        return sqlName;
    }


    /**
     * 根据前端传的集合生成查询条件
     * [{
     * "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 = "";
                if (paramBean.getValue() instanceof Boolean) {
                    value = paramBean.getValue().toString();
                } else {
                    value = paramBean.getValue().toString();
                }
                if (StringUtils.isBlank(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 {
//        String exportflag = request.getHeader("exportflag");
//        if(StringUtils.isBlank(exportflag)){
//            String bodyString = HttpContextUtils.getBodyString(request);
//            log.info("【通用导出参数】:{}",bodyString);
//            ExportLogic.add(request,bodyString);
//            return;
//        }

        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.isEmpty()) {
                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) {
                    Field field = fieldMap.get(f);
                    field.setAccessible(true);
                    String jName = field.getName();
                    if (!jList.isEmpty() && !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();
                    if (typeName.equals("java.util.List") || typeName.equals("java.util.Map")) {
                        continue;
                    }
                    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("getRemark");
                            Object invoke = getKey.invoke(enumO);
                            //String enumKey =  invoke+"";
                            String enumValue = (String) getValue.invoke(enumO);
                            if (value != null && value.toString().equals(enumO.toString())) {
                                value = enumValue;
                            }
                        }
                    }

//                    DictConversion dictConversion = field.getAnnotation(DictConversion.class);
//                    if (dictConversion != null  && value instanceof String && StringUtils.isNotBlank((String) value)) {
//                        value = DictUtils.getDictDataValue(dictConversion.value(), (String) value);
//                    }

                    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 if (field.getType().isAssignableFrom(LocalDateTime.class)) {
                            row.add(LocalDateTimeUtil.formatNormal((LocalDateTime) value));
                        } else if (field.getType().isAssignableFrom(LocalDate.class)) {
                            row.add(LocalDateTimeUtil.formatNormal((LocalDate) 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();

            //样式
            WriteCellStyle cellStyle = writeCellStyle();

            //必须放到循环外，否则会刷新流
            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();
            }
        }
    }


    /**
     * excel样式
     *
     * @return
     */
    public static WriteCellStyle writeCellStyle() {
        //样式
        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);
        return cellStyle;
    }


}
