package cn.iocoder.yudao.module.knowledge.utils;

import cn.iocoder.yudao.module.knowledge.annotation.ExcelOut;

import cn.iocoder.yudao.module.knowledge.constants.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.tomcat.util.http.parser.Authorization;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Component
public class ExcelUtils {





    public static String PRIVATE_KEY;
    public static String REQUEST_TOKEN;


    private final static Logger logger = LoggerFactory.getLogger(ExcelUtils.class);

    /**
     * @param list     导出的数据list
     * @param clazz    导出的对象class
     * @param fileName 导出的文件名称
     * @param response response
     * @param <T>
     */
    public static <T> void exportExcel(List<T> list, Class<T> clazz, String fileName, HttpServletResponse response) {
        SXSSFWorkbook workBook = getWorkBook(list, clazz);
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");  // .xlsx 用这个
            //response.setContentType("application/vnd.ms-excel;charset=UTF-8");  // .xls 用这个
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
            OutputStream output = response.getOutputStream();
            BufferedOutputStream bufferedOutPut = new BufferedOutputStream(output);
            workBook.write(bufferedOutPut);
            bufferedOutPut.flush();
            bufferedOutPut.close();
            output.close();
            workBook.close();
        } catch (Exception e) {
            //记录日志
            log.info(e.getMessage());
            //throw new BadRequestException(e.getMessage());
        }
    }

    private static <T> SXSSFWorkbook getWorkBook(List<T> list, Class<T> clazz) {
        SXSSFWorkbook wb = new SXSSFWorkbook(100);
        // 单元格样式
        CellStyle titleStyle = titleStyle(wb);// 表头样式
        CellStyle orderStyle = orderStyle(wb);// 序号样式
        CellStyle contentStyle = contentStyle(wb);// 普通单元格样式

        // 遍历集合数据，产生数据行
        Iterator<?> it = list.iterator();
        int index = 0;//数据条数（行数）
        int rowIndex = 0;// 每新建一个sheet此数值归零
        Sheet sheet = null;
        while (it.hasNext()) {
            if (index == 0 || index % 5000 == 0) {//此处是限制单个sheet数据量，每5000条数据新建一个sheet
                rowIndex = 0;
                //创建新的sheet
                sheet = wb.createSheet();
                Row row = sheet.createRow(0);
                row.setHeightInPoints(23);
                Field[] declaredFields = getAllFields(clazz);
                //添加序号
                Cell cellOrder = row.createCell(0);
                cellOrder.setCellValue("序号");
                cellOrder.setCellStyle(titleStyle);
                sheet.setColumnWidth(0, 10 * 256);
                //设置表头
                for (Field field : declaredFields) {
                    ExcelOut annotation =         field.getAnnotation(ExcelOut.class);
                    if (annotation != null) {
                        Cell cell = row.createCell(Integer.valueOf(annotation.sort()));
                        cell.setCellValue(annotation.name());
                        cell.setCellStyle(titleStyle);
                        //设置列的宽度
                        sheet.setColumnWidth(Integer.valueOf(annotation.sort()), (int) annotation.width() * 256);
                    }
                }
            }

            index++;
            rowIndex++;

            //设置序号值
            Row row = sheet.createRow(rowIndex);
            Cell cell = row.createCell(0);
            cell.setCellStyle(orderStyle);
            //cell.setCellValue(index);//新的sheet序号接着上个sheet的序号累加
            cell.setCellValue(rowIndex);//新的sheet序号从头开始累加
            row.setHeightInPoints(18);
            T t = (T) it.next();
            // 利用反射，根据javabean属性的先后顺序，动态调用getXxx()方法得到属性值
            Field[] fields = getAllFields(t.getClass());
            for (Field field : fields) {
                ExcelOut annotation = field.getAnnotation(ExcelOut.class);
                if (annotation != null) {
                    cell = row.createCell(Integer.valueOf(annotation.sort()));
                    cell.setCellStyle(contentStyle);
                    String fieldName = field.getName();
                    String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    try {
                        Method getMethod = t.getClass().getMethod(getMethodName, new Class[]{});
                        Object value = getMethod.invoke(t, new Object[]{});
                        String dateFormat = annotation.dateFormat();
                        Boolean isFileUrl = annotation.isFileUrl();
                        if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
                        {

                            cell.setCellValue(parseDateToStr(dateFormat, value));
                        }else if(isFileUrl){
                            cell.setCellValue(PRIVATE_KEY + value + REQUEST_TOKEN);
                        }else {
                            cell.setCellValue(null == value ? "" : String.valueOf(value));
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return wb;
    }
    /**
     * 格式化不同类型的日期对象
     *
     * @param dateFormat 日期格式
     * @param val 被格式化的日期对象
     * @return 格式化后的日期字符
     */
    public static String parseDateToStr(String dateFormat, Object val)
    {
        if (val == null)
        {
            return "";
        }
        String str;
        if (val instanceof Date)
        {
            str = DateUtils.parseDateToStr(dateFormat, (Date) val);
        }
        else if (val instanceof LocalDateTime)
        {
            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));
        }
        else if (val instanceof LocalDate)
        {
            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));
        }
        else
        {
            str = val.toString();
        }
        return str;
    }
    //设置标题样式
    private static CellStyle titleStyle(SXSSFWorkbook wb) {
        CellStyle cellStyle = contentStyle(wb);
        //设置居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        //设置单元格背景颜色
        cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        //设置单元格填充样式(使用纯色背景颜色填充)
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        //设置字体加粗
        Font font = wb.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 12);
        //设置字体
        font.setFontName("宋体");
        cellStyle.setFont(font);
        return cellStyle;
    }

    //设置单元格样式
    private static CellStyle contentStyle(SXSSFWorkbook wb) {
        //给单元格设置样式
        CellStyle cellStyle = wb.createCellStyle();
        Font font = wb.createFont();
        // 设置字体大小
        font.setFontHeightInPoints((short) 11);
        // 设置字体
        font.setFontName("宋体");
        // 给字体设置样式
        cellStyle.setFont(font);
        // 字体设置边框
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        // 设置垂直居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        return cellStyle;
    }

    // 设置单元格样式
    private static CellStyle orderStyle(SXSSFWorkbook wb) {
        CellStyle cellStyle = contentStyle(wb);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        return cellStyle;
    }

    private static <T> Field[] getAllFields(Class<T> clazz) {
        Field[] declaredFields = clazz.getDeclaredFields();
        Class superClass = clazz.getSuperclass();
        if (!superClass.equals(Object.class)) {
            Field[] superFields = superClass.getDeclaredFields();
            int sonLength = declaredFields.length;
            int superLength = superFields.length;
            // 合并两个数组
            Field[] newFields = new Field[sonLength + superLength];
            System.arraycopy(declaredFields, 0, newFields, 0, sonLength);
            System.arraycopy(superFields, 0, newFields, sonLength, superLength);
            return newFields;
        }
        return declaredFields;
    }

    private static final String XLS = "xls";
    private static final String XLSX = "xlsx";

    /**
     * 根据文件后缀名类型获取对应的工作簿对象
     *
     * @param inputStream 读取文件的输入流
     * @param fileType    文件后缀名类型（xls或xlsx）
     * @return 包含文件数据的工作簿对象
     * @throws IOException
     */
    public static Workbook getWorkbook(InputStream inputStream, String fileType) throws IOException {
        Workbook workbook = null;
        if (fileType.equalsIgnoreCase(XLS)) {
            workbook = new HSSFWorkbook(inputStream);
        } else if (fileType.equalsIgnoreCase(XLSX)) {
            workbook = new XSSFWorkbook(inputStream);
        }
        return workbook;
    }

    /**
     * 导入Excel
     */
    public static Workbook importExcel(MultipartFile file) throws IOException {
        //读取文件流
        InputStream is = file.getInputStream();
        //文件名
        String fileName = file.getOriginalFilename();
        //判断文件格式是否为excel
        boolean notNull = false;
        if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
            //throw new BusinessException("上传文件格式不正确");
        }
        Workbook wb = null;
        if (fileName.matches("^.+\\.(?i)(xlsx)$")) {
            //xlsx格式
            wb = new XSSFWorkbook(is);
        } else {
            //xls格式
            wb = new HSSFWorkbook(is);
        }
        return wb;
    }
}
