package com.jjt.utils;

import cn.hutool.json.JSONArray;
import com.jjt.common.utils.StringUtils;
import javafx.util.Pair;
import org.apache.poi.ss.usermodel.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.*;
import java.util.*;

/**
 * Tool$
 *
 * @author wukai
 * @date 2024/5/12 02:18
 */
public class Tools {
    /**
     * 允许的告警编号
     * Alarm_unit_1	BOOL	GB1伺服通讯故障
     * Alarm_unit_2	BOOL	GB2伺服通讯故障
     * Alarm_unit_3	BOOL	GB3伺服通讯故障
     * Alarm_unit_4	BOOL	GB4伺服通讯故障
     * Alarm_unit_5	BOOL	GB5伺服通讯故障
     * Alarm_unit_6	BOOL	牵引伺服通讯故障
     * Alarm_unit_7	BOOL	卷曲伺服通讯故障
     * Alarm_unit_8	BOOL	断纱故障
     * Alarm_unit_9	BOOL	电源故障
     * Alarm_unit_10	BOOL	联动未完全啮合
     * Alarm_unit_11	BOOL	米长到达
     * Alarm_unit_12	BOOL	GB1剩余圈数报警
     * Alarm_unit_13	BOOL	GB2剩余圈数报警
     * Alarm_unit_14	BOOL	GB3剩余圈数报警
     * Alarm_unit_15	BOOL	GB4剩余圈数报警
     * Alarm_unit_16	BOOL	GB5剩余圈数报警
     * Alarm_unit_17	BOOL	主轴变频掉线
     * Alarm_unit_18	BOOL	主轴变频故障
     * Alarm_unit_19	BOOL	GB1伺服故障
     * Alarm_unit_20	BOOL	GB2伺服故障
     * Alarm_unit_21	BOOL	GB3伺服故障
     * Alarm_unit_22	BOOL	GB4伺服故障
     * Alarm_unit_23	BOOL	GB5伺服故障
     * Alarm_unit_24	BOOL	牵引伺服故障
     * Alarm_unit_25	BOOL	卷曲伺服故障
     * Alarm_unit_26	BOOL	拍照自停
     * Alarm_unit_27	UINT	CCD位置报警信息显示
     */
    public static final int[] ALLOW_ALARM = {8, 10, 11, 12, 13, 14, 15, 16, 17, 18};

    /**
     * 查找编号是否允许告警
     *
     * @param x 编号
     * @return
     */
    public static boolean findAllow(int x) {
        OptionalInt result = Arrays.stream(ALLOW_ALARM)
                .filter(element -> element == x)
                .findFirst();
        return result.isPresent();
    }

    public static void main(String[] args) {
        int p = 12;
        LocalDateTime ldt = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        System.err.println(ldt.plusHours(2 * p));
    }

    /**
     * BigDecimal转float保留两位小数
     *
     * @param v BigDecimal
     * @return float
     */
    public static float scale2(BigDecimal v) {
        return v.setScale(2, RoundingMode.HALF_UP).floatValue();
    }

    /**
     * 计算稼动率
     * a/(a+b)*100 保留两位小数
     *
     * @param a 开机时间
     * @param b 关机时间
     * @return 稼动率
     */
    public static BigDecimal calcPercent(BigDecimal a, BigDecimal b) {
        if (a != null && a.intValue() != 0) {
            return a.multiply(BigDecimal.valueOf(100)).divide(a.add(b), 2, RoundingMode.HALF_UP);
        } else {
            return BigDecimal.ZERO;
        }
    }

    public static Map<String, Object> json2Map(JSONArray values, String table) {
        Map<String, Object> dataMap = new HashMap<>(16);

        for (int i = 0; i < values.size(); i++) {
            JSONArray d = values.getJSONArray(i);
            String key = d.getStr(0).replace(table + ".", "");

            switch (d.getStr(2)) {
                case "FLOAT":
                    dataMap.put(key, d.getFloat(1));
                    break;
                case "BOOLEAN":
                    dataMap.put(key, d.getBool(1));
                    break;
                case "INT32":
                case "INT64":
                    dataMap.put(key, d.getInt(1));
                    break;
            }
        }
        return dataMap;
    }

    /**
     * 当前的整点时间，0分0秒0毫秒
     *
     * @return LocalDateTime
     */
    public static LocalDateTime currWholeTime() {
        return LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 按照给定日期，按2小时分段,组成where条件
     *
     * @param date yyyy-mm-dd
     * @return long[][]
     */
    public static List<String> getTimesWhere(String date) {
        List<String> resultList = new ArrayList<>();
        LocalDate localDate = LocalDate.parse(date);
        //获取今天0点
        LocalDateTime ldt = LocalDateTime.of(localDate, LocalTime.MIN);
        LocalDateTime start = ldt;
        LocalDateTime end = start.plusHours(2);
        do {
            //-1秒 把上一轮最后一条数据取出来
            start = start.minusSeconds(1);
            Long startTime = start.toInstant(ZoneOffset.of("+8")).toEpochMilli();
            Long endTime = end.toInstant(ZoneOffset.of("+8")).toEpochMilli();
            System.err.println(start + "\t" + end);
            String where = String.format("time>%s and time <=%s", startTime, endTime);
            resultList.add(where);
            start = end;
            end = start.plusHours(2);
        } while (!ldt.plusDays(1).isBefore(end));
        return resultList;
    }

    /**
     * 获取指定日期的时间分段
     * map.put("period", period + "");
     * map.put("start", startTime);
     * map.put("end", endTime);
     *
     * @param date 字符串格式的 yyyy-mm-dd
     * @return list map
     */
    public static List<Pair<LocalDateTime, LocalDateTime>> timePeriod(String date) {
        LocalDate localDate = LocalDate.parse(date);
        return timePeriod(localDate);
    }


    /**
     * 获取指定日期的时间分段 早上7点至第二天7点（不含）
     * 每小时一条
     *
     * @param date LocalDate yyyy-mm-dd
     * @return LocalDateTime map.get("start") map.get("end")
     */
    public static List<Pair<LocalDateTime, LocalDateTime>> timePeriod(LocalDate date) {
        List<Pair<LocalDateTime, LocalDateTime>> result = new ArrayList<>();
        LocalDateTime ldt = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime start = ldt.plusHours(7);
        for (int i = 0; i < 24; i++) {
            //需要减少1秒，取出上一轮最后一条数据
            Pair<LocalDateTime, LocalDateTime> pair = new Pair<>(start.minusSeconds(1), start.plusHours(1));
            result.add(pair);
            start = start.plusHours(1);
        }
        return result;
    }

    /**
     * 获取当日 直到当前时间的上一个偶数时间
     *
     * @return LocalDateTime map.get("start") map.get("end")
     */
    public static List<Pair<LocalDateTime, LocalDateTime>> timePeriod() {
        List<Pair<LocalDateTime, LocalDateTime>> result = new ArrayList<>();
        //获取当天0点
        LocalDateTime ldt = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0);
        LocalDateTime start = ldt.withHour(0);
        LocalDateTime end = start.plusHours(1);
        LocalDateTime stop = ldt.minusHours(1);
        do {
            //-1秒 把上一轮最后一条数据取出来
            start = start.minusSeconds(1);
            Pair<LocalDateTime, LocalDateTime> pair = new Pair<>(start, end);
            result.add(pair);
            start = end;
            end = start.plusHours(1);
        } while (!end.isAfter(stop));
        return result;
    }

    /**
     * 获取统计时间 当天7天至第二天7点（不含）
     *
     * @param date 字符串类型的时间
     * @return 开始结束时间对
     */
    public static Pair<Date, Date> calcDay(String date) {
        LocalDate localDate = LocalDate.parse(date);
        return calcDay(localDate);
    }

    /**
     * 获取统计时间 当天7天至第二天7点（不含）
     *
     * @param localDate 日期
     * @return 开始结束时间对
     */
    public static Pair<Date, Date> calcDay(LocalDate localDate) {
        LocalDateTime ldt = LocalDateTime.of(localDate, LocalTime.MIN);
        LocalDateTime start = ldt.plusHours(7);
        LocalDateTime end = start.plusHours(23).plusMinutes(59).plusSeconds(59);
        Date sTime = Date.from(start.atZone(ZoneId.systemDefault()).toInstant());
        Date eTime = Date.from(end.atZone(ZoneId.systemDefault()).toInstant());
        return new Pair<>(sTime, eTime);
    }

    /**
     * 获取统计时间 当天7天至结束时间（不含）
     *
     * @return 开始结束时间对
     */
    public static Pair<Date, Date> calcToday() {
        LocalDateTime end = LocalDateTime.now();
        LocalDate localDate = end.toLocalDate();
        if (end.getHour() < 7) {
            //如果小于7点，则表示生产时间是前一天
            localDate = localDate.minusDays(1);
        }
        //生产时间从7点开始
        LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN).plusHours(7);
        Date sTime = Date.from(start.atZone(ZoneId.systemDefault()).toInstant());
        Date eTime = Date.from(end.atZone(ZoneId.systemDefault()).toInstant());
        return new Pair<>(sTime, eTime);
    }

    /**
     * 获取A班开始结束时间
     *
     * @param localDate 日期
     * @return A班时间
     */
    public static Pair<Date, Date> teamA(LocalDate localDate) {
        LocalDateTime ldt = LocalDateTime.of(localDate, LocalTime.MIN);
        LocalDateTime start = ldt.plusHours(7);
        LocalDateTime end = start.plusHours(12).minusSeconds(1);
        Date sTime = Date.from(start.atZone(ZoneId.systemDefault()).toInstant());
        Date eTime = Date.from(end.atZone(ZoneId.systemDefault()).toInstant());
        return new Pair<>(sTime, eTime);
    }

    /**
     * 获取B班开始结束时间
     *
     * @param localDate 日期
     * @return A班时间
     */
    public static Pair<Date, Date> teamB(LocalDate localDate) {
        LocalDateTime ldt = LocalDateTime.of(localDate, LocalTime.MIN);
        LocalDateTime start = ldt.plusHours(19);
        LocalDateTime end = start.plusHours(12).minusSeconds(1);
        Date sTime = Date.from(start.atZone(ZoneId.systemDefault()).toInstant());
        Date eTime = Date.from(end.atZone(ZoneId.systemDefault()).toInstant());
        return new Pair<>(sTime, eTime);
    }

    /**
     * 复制sheet
     *
     * @param sourceSheet 源sheet
     * @param targetSheet 目录sheet
     * @param workbook    wb
     */
    public static void copyTitleRowWithStyles(Sheet sourceSheet, Sheet targetSheet, Workbook workbook) {
        if (sourceSheet != null && targetSheet != null) {
            Row sourceRow = sourceSheet.getRow(0);
            // 假设标题行在第0行
            if (sourceRow != null) {
                Row targetRow = targetSheet.createRow(0);
                for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
                    Cell sourceCell = sourceRow.getCell(i);
                    Cell targetCell = targetRow.createCell(i);
                    // 复制单元格值
                    targetCell.setCellValue(sourceCell.getStringCellValue());
                    // 复制单元格样式
                    CellStyle sourceCellStyle = sourceCell.getCellStyle();
                    CellStyle targetCellStyle = workbook.createCellStyle();
                    targetCellStyle.cloneStyleFrom(sourceCellStyle);
                    targetCell.setCellStyle(targetCellStyle);
                }
            }
        }
    }

    /**
     * 秒转换成 xxx分xx秒
     *
     * @param x x
     * @return 结果
     */
    public static String convertMS(long x) {
        long minutes = x / 60;
        // 分钟差
        long seconds = x % 60;
        // 秒数差，不包括分钟的秒数
        return minutes + "分" + (seconds > 10 ? seconds : "0" + seconds) + "秒";
    }

    /**
     * 秒转换成 xxx分xx秒
     *
     * @param x x
     * @return 结果
     */
    public static String convertHMS(long x) {
        long hour = x / 60 / 60;
        long minutes = x / 60 % 60;
        // 分钟差
        long seconds = x % 60;
        // 秒数差，不包括分钟的秒数
        String str = minutes + "分" + (seconds > 10 ? seconds : "0" + seconds) + "秒";
        if (hour > 0) {
            str = hour + "小时" + str;
        }
        return str;
    }

    /**
     * 密度合并
     * 1.小于0.3舍弃
     * 2.大于0.7进位
     * 3.介于中间则取0.5
     *
     * @param density 密度
     * @return 合并后结果
     */
    public static BigDecimal density(BigDecimal density) {
        int intValue = density.intValue();
        // 余1得到小数部分
        BigDecimal decimalPart = density.remainder(BigDecimal.ONE);
        BigDecimal res;
        if (decimalPart.compareTo(BigDecimal.valueOf(0.3)) < 0) {
            //小于0.3舍弃
            res = BigDecimal.valueOf(intValue);
        } else if (decimalPart.compareTo(BigDecimal.valueOf(0.7)) > 0) {
            //大于0.7 进位
            res = BigDecimal.valueOf(intValue + 1);
        } else {
            //介于0.3-0.7 取0.5
            res = BigDecimal.valueOf(intValue).add(BigDecimal.valueOf(0.5));
        }
        res = res.setScale(2, RoundingMode.HALF_UP);
        return res;
    }


    /**
     * 组装设备ID
     */
    public static void comboStop(int i, int[] nums, String[] devices, long id) {
        nums[i]++;
        if (StringUtils.isNotEmpty(devices[i])) {
            devices[i] += ",";
        } else {
            devices[i] = "";
        }
        devices[i] += id;
    }


    /**
     * 生成在平均值附近波动的随机比例
     *
     * @param parts 要分割的份数
     * @return 每份的比例数组（总和为1）
     */
    public static BigDecimal[] balancedRandomSplit(int parts) {
        Random random = new Random();
        int SCALE = 4; // 保留4小数位数
        double DEVIATION_PERCENT = 0.25; // 波动幅度

        if (parts <= 0) {
            throw new IllegalArgumentException("份数必须大于0");
        }

        BigDecimal average = BigDecimal.ONE.divide(new BigDecimal(parts), SCALE + 2, RoundingMode.HALF_UP);
        BigDecimal[] proportions = new BigDecimal[parts];
        BigDecimal remaining = BigDecimal.ONE;

        // 前parts-1次分割
        for (int i = 0; i < parts - 1; i++) {
            // 计算允许的波动范围
            BigDecimal min = average.multiply(BigDecimal.ONE.subtract(new BigDecimal(DEVIATION_PERCENT)))
                    .max(BigDecimal.ZERO);
            BigDecimal max = average.multiply(BigDecimal.ONE.add(new BigDecimal(DEVIATION_PERCENT)));

            // 确保剩余比例足够
            BigDecimal adjustedMax = remaining.subtract(min.multiply(new BigDecimal(parts - i - 1)));
            max = max.min(adjustedMax);

            // 在范围内随机取值
            BigDecimal range = max.subtract(min);
            BigDecimal randomFactor = BigDecimal.valueOf(random.nextDouble());
            BigDecimal proportion = min.add(range.multiply(randomFactor))
                    .setScale(SCALE, RoundingMode.HALF_UP);

            proportions[i] = proportion;
            remaining = remaining.subtract(proportion);
        }

        // 最后一份
        proportions[parts - 1] = remaining.setScale(SCALE, RoundingMode.HALF_UP);

        return proportions;
    }

    /**
     * BigDecimal空数据处理
     *
     * @param v 数据
     * @return 结果
     */
    public static BigDecimal nullProcess(BigDecimal v) {
        return v == null ? BigDecimal.ZERO : v;
    }

}
