package com.ruoyi.common.zqt;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.apache.poi.ss.usermodel.CellType.STRING;


public class ZQTPoiUtil {

    private static DecimalFormat df = new DecimalFormat("0");// 格式化数字
    private static DecimalFormat num2 = new DecimalFormat("0.00");
    private static DecimalFormat num6 = new DecimalFormat("0.000000");
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化日期字符串
    private static SimpleDateFormat sdfYmd = new SimpleDateFormat("yyyy-MM-dd");
    private static SimpleDateFormat formartYmd = new SimpleDateFormat("yyyy/MM/dd");

    /**
     * 获得Workbook工作簿
     *
     * @param file
     * @return
     * @throws IOException
     */
    public static Workbook createWorkbook(MultipartFile file) throws IOException {
        String fileName = file.getOriginalFilename();
        String ext = FilenameUtils.getExtension(fileName);

        Workbook wb = null;

        if (ext.equals("xlsx")) {
            wb = new XSSFWorkbook(file.getInputStream());

        } else if (ext.equals("xls")) {
            wb = new HSSFWorkbook(file.getInputStream());
        }

        return wb;
    }

    /**
     * 获得公式执行器
     *
     * @param wb
     * @return
     */
    public static FormulaEvaluator createFormulaEvaluator(Workbook wb) {

        FormulaEvaluator fe = null;

        if (wb instanceof XSSFWorkbook) {
            fe = new XSSFFormulaEvaluator((XSSFWorkbook) wb);
        } else if (wb instanceof HSSFWorkbook) {
            fe = new HSSFFormulaEvaluator((HSSFWorkbook) wb);
        }
        return fe;
    }

    /**
     * 获得单元格值
     *
     * @param cell
     * @return
     */
    public static String getCellValue(Cell cell) {
        return getCellValue(cell, null);
    }

    public static Integer getCellValueToInt(Cell cell) {
        String val = getCellValue(cell);
        if (StringUtils.isNotBlank(val)) {
            return Integer.valueOf(getCellValue(cell));
        }
        return null;
    }

    /**
     * 获得单元格值，并进行校验
     *
     * @param cell
     * @param checkType 校验类型
     * @param rownum    当前行号
     * @param columnnum 当前列数
     * @param sb
     * @return
     */
    public static String getCellValue(Cell cell, String checkType, int rownum, int columnnum, StringBuilder sb) {

        String cellValue = getCellValue(cell);
        return checkCellValue(checkType, cellValue, rownum, columnnum, sb);

    }

    /**
     * 获得单元格值，并进行校验，包含公式处理
     *
     * @param cell
     * @param fe
     * @param checkType 校验类型
     * @param rownum    当前行号
     * @param columnnum 当前列数
     * @param sb
     * @return
     */
    public static String getCellValue(Cell cell, FormulaEvaluator fe, String checkType, int rownum, int columnnum, StringBuilder sb) {

        String cellValue = getCellValue(cell, fe);
        return checkCellValue(checkType, cellValue, rownum, columnnum, sb);

    }

    /**
     * 获得单元格值
     *
     * @param cell
     * @param fe
     * @return
     */
    public static String getCellValue(Cell cell, FormulaEvaluator fe) {

        String val = "";

        if (cell != null) {
            switch (cell.getCellType()) {
                case STRING:
                    if (StringUtils.isNotBlank(cell.getStringCellValue())) {
                        val = cell.getStringCellValue();
                    }
                    break;
                case NUMERIC:
                    if ("@".equals(cell.getCellStyle().getDataFormatString())) {
                        val = df.format(cell.getNumericCellValue());
                    } else if ("General".equals(cell.getCellStyle().getDataFormatString())) {
                        val = df.format(cell.getNumericCellValue());
                    } else if (cell.getCellStyle().getDataFormatString() != null && cell.getCellStyle().getDataFormatString().startsWith("0.00_")) {
                        val = num2.format(cell.getNumericCellValue());
                    } else if (cell.getCellStyle().getDataFormatString() != null && cell.getCellStyle().getDataFormatString().equals("0.00")) {
                        val = num2.format(cell.getNumericCellValue());
                    } else if (cell.getCellStyle().getDataFormatString() != null && cell.getCellStyle().getDataFormatString().startsWith("0.000000_")) {
                        val = num6.format(cell.getNumericCellValue());
                    } else if (cell.getCellStyle().getDataFormatString() != null && cell.getCellStyle().getDataFormatString().startsWith("0_")) {
                        val = df.format(cell.getNumericCellValue());
                    } else {
                        val = formartYmd.format(HSSFDateUtil.getJavaDate(cell.getNumericCellValue()));
                    }
                    break;
                case BOOLEAN:
                    val = String.valueOf(cell.getBooleanCellValue());
                    break;
                case FORMULA:
                    val = fe.evaluate(cell).getStringValue();
                    break;
                case BLANK:
                    val = "";
                    break;
                default:
                    val = cell.toString();
                    break;
            }
        }

        return val;
    }

    public static final String CHECK_TYPE_INT = "int";
    public static final String CHECK_TYPE_BIGDECIMAL = "bigDecimal";
    public static final String CHECK_TYPE_DATE = "date";
    public static final String CHECK_TYPE_MOBILE = "mobile";
    public static final String CHECK_TYPE_EMAIL = "email";

    /**
     * 校验格式
     *
     * @param type
     * @param value
     * @param rownum
     * @param columnnum
     * @param sb
     * @return
     */
    public static String checkCellValue(String type, String value, int rownum, int columnnum, StringBuilder sb) {
        String regex = "";

        switch (type) {
            case CHECK_TYPE_INT:
                regex = "^\\d+$";
                if (!Pattern.matches(regex, value)) {
                    sb.append("第" + rownum + "行，第" + columnnum + "列数据有误，" + value + " 不符合整数要求\n");
                }
                break;
            case CHECK_TYPE_BIGDECIMAL:
                regex = "^\\d+(\\.\\d+)?$";
                if (!Pattern.matches(regex, value)) {
                    sb.append("第" + rownum + "行，第" + columnnum + "列数据有误，" + value + " 不符合浮点数要求\n");
                }
                break;
            case CHECK_TYPE_DATE:
                regex = "^\\d{4}/\\d{2}/\\d{2}$";
                if (!Pattern.matches(regex, value)) {
                    sb.append("第" + rownum + "行，第" + columnnum + "列数据有误，" + value + " 不符合日期格式\n");
                }
                break;
            case CHECK_TYPE_MOBILE:
                if (StringUtils.isNotBlank(value) && !isMobile(value)) {
                    sb.append("第" + rownum + "行，第" + columnnum + "列数据有误，" + value + " 不符合手机格式\n");
                }
                break;
            case CHECK_TYPE_EMAIL:
                if (StringUtils.isNotBlank(value) && !isEmail(value)) {
                    sb.append("第" + rownum + "行，第" + columnnum + "列数据有误，" + value + " 不符合邮箱格式\n");
                }
                break;
        }
        return value;
    }

    /**
     * 创建上传日志文件
     *
     * @param uploadLogDir
     * @return
     * @throws IOException
     */
    public static File createLogFile(String uploadLogDir) throws IOException {
        String logFileName = convertDateToStringFormat(new Date(), "yyyy-MM-dd-HH-mm-ss") + ".txt";
        File logFile = new File(uploadLogDir + logFileName);
        if (!logFile.exists()) {
            logFile.createNewFile();
        }
        return logFile;
    }


    public static File writeLogFile(String uploadLogDir, StringBuilder sb) throws IOException {
        File logFile = createLogFile(uploadLogDir);
        FileUtils.writeStringToFile(logFile, sb.toString(), "UTF-8");
        return logFile;
    }

    /**
     * 导出Excel通用方法
     *
     * @param name
     * @param response
     * @throws Exception
     */
    public static void export(String name, List beanList, String colModelNames, String colNames, HttpServletResponse response) {

        List<String> colModelNameList = tokenize(colModelNames, ",");
        List<String> colNameList = tokenize(colNames, ",");

        //创建工作簿
        XSSFWorkbook workBook = new XSSFWorkbook();
        //创建工作表
        XSSFSheet sheet = workBook.createSheet(name);

        // 第1行，表头
        XSSFRow row_0 = sheet.createRow(0);
        row_0.setHeight((short) 500);
        for (int columnIndex = 0; columnIndex < colModelNameList.size(); columnIndex++) {
            XSSFCell cell = row_0.createCell(columnIndex, STRING);
            cell.setCellValue(colNameList.get(columnIndex));
            sheet.setColumnWidth(columnIndex, 5000);
        }

        // 从第2行开始，导出数据
        if (null != beanList && beanList.size() > 0) {
            for (int rowIndex = 1; rowIndex <= beanList.size(); rowIndex++) {
                try {
                    // 从第2行开始导出数据
                    XSSFRow row = sheet.createRow(rowIndex);
                    Object item = beanList.get(rowIndex - 1);

                    for (int columnIndex = 0; columnIndex < colModelNameList.size(); columnIndex++) {
                        String column = colModelNameList.get(columnIndex);
                        String value = "";
                        try {
                            value = (String) BeanUtils.getProperty(item, column);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        row.createCell(columnIndex, STRING).setCellValue(value);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        OutputStream output = null;
        try {
            output = response.getOutputStream();
            String fileName = name + getExportDate() + ".xlsx";
            fileName = new String(fileName.getBytes("gb2312"), "iso8859-1");

            response.reset();
            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/msexcel");
            workBook.write(output);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                output.flush();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static List<String> tokenize(final String s, final String delim) {
        List<String> res = new ArrayList<>();
        if (null != s && null != delim && s.trim().length() > 0) {
            StringTokenizer strTokenizer = new StringTokenizer(s, delim);

            while (strTokenizer.hasMoreTokens()) {
                res.add(strTokenizer.nextToken().trim());
            }
        }
        return res;
    }

    public static String convertDateToStringFormat(Date aDate, String format) {
        return getDateTime(format, aDate);
    }

    /**
     * This method generates a string representation of a date's date/time
     * in the format you specify on input
     *
     * @param aMask the date pattern the string is in
     * @param aDate a date object
     * @return a formatted string representation of the date
     * @see SimpleDateFormat
     */
    public static String getDateTime(String aMask, Date aDate) {

        SimpleDateFormat df = null;
        String returnValue = "";

        if (aDate != null) {
            df = new SimpleDateFormat(aMask);
            returnValue = df.format(aDate);
        }

        return (returnValue);
    }

    public static String getExportDate() {
        return convertDateToStringFormat(new Date(), "yyyyMMdd");
    }

    public static boolean isMobile(final String number) {
        String mobileFormat = "^((13[0-9])|(14[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$";
        Pattern pattern = Pattern.compile(mobileFormat);
        Matcher matcher = pattern.matcher(number);
        return matcher.matches();
    }

    public static boolean isEmail(String value) {
        String parrern = "[a-zA-Z0-9_\\-\\.]+@[a-zA-Z0-9]+(\\.(com|cn|org|edu|hk|net|com\\.cn))";
        Pattern p = Pattern.compile(parrern);
        Matcher m = p.matcher(value);
        return m.matches();
    }
}
