package com.ruoyi.common.utils.collection;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.ruoyi.common.utils.DateUtils;

/**
 * @author wjh
 * 2024/4/29
 */
public class CollectionUtils extends org.springframework.util.CollectionUtils {

    /**
     * 是否为空，包括空集合、空元素
     * @param collection
     * @return
     */
    public static boolean isEmptyElement(Collection<?> collection) {
        if (collection == null) {
            return true;
        }
        // 过滤空元素
        List<?> collect = collection.stream().filter(Objects::nonNull).collect(Collectors.toList());
        return org.springframework.util.CollectionUtils.isEmpty(collect);
    }

    public static boolean isNotEmptyElement(Collection<?> collection) {
        return !isEmptyElement(collection);
    }

    /**
     * 填充列表中的元素
     * @param oldList 旧列表
     * @param startDate 起始日期
     * @param endDate 结束日期
     * @param clazz 填充元素的类
     */
    public static List<? super FillAble> fillElement(List<? extends FillAble> oldList, Date startDate, Date endDate,Class<? extends FillAble> clazz ) throws InstantiationException, IllegalAccessException {
        List<FillAble> newList = new ArrayList<>();
        while (startDate.compareTo(endDate) <= 0) {
            Date finalI = startDate;
            FillAble fillable = oldList.stream().filter(item -> DateUtils.getYYYY_MM_DD(item.fillDate()).equals(DateUtils.getYYYY_MM_DD(finalI))).findFirst().orElse(null);
            if (fillable == null) {
                fillable = clazz.newInstance().fillElement(finalI);
            }
            newList.add(fillable);
            startDate = DateUtils.addDays(startDate,1);
        }
        return newList;
    }


    // TODO 未经验证
    /**
     * 补全列表中的日期数据，对于缺失的日期使用提供的工厂方法生成对象。
     *
     * @param dataList        原始数据列表，元素需有getDateMethod返回Date类型属性的方法
     * @param dateGetter      获取元素日期属性的方法引用
     * @param customFactory   当日期缺失时，用于生成对应元素的对象工厂方法
     * @param <T>             数据列表中元素的类型
     * @return 完整日期序列下的数据列表，缺失的日期已由customFactory填充
     */
    public static <T> List<T> completeMissingDates(List<T> dataList,
                                                   Function<T, Date> dateGetter,
                                                   Function<Date, T> customFactory) {
        if (dataList == null || dataList.isEmpty()) {
            return Collections.emptyList();
        }

        List<Date> dates = dataList.stream()
                .map(dateGetter)
                .sorted()
                .collect(Collectors.toList());

        Date startDate = dates.get(0);
        Date endDate = dates.get(dates.size() - 1);

        List<Date> fullDates = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);

        while (!calendar.getTime().after(endDate)) {
            fullDates.add(calendar.getTime());
            calendar.add(Calendar.DATE, 1);
        }

        Map<Date, T> dateToObjectMap = dataList.stream()
                .collect(Collectors.toMap(dateGetter, Function.identity()));

        return fullDates.stream()
                .map(date -> dateToObjectMap.getOrDefault(date, customFactory.apply(date)))
                .collect(Collectors.toList());
    }

    public static boolean isNotEmpty(List<?> deviceList) {
        return !isEmpty(deviceList);
    }

    /**
     * 集合映射，去除空值、重复值
     */
    public static <T, R> List<R> map(List<T> list, Function<? super T, ? extends R> keyMapper) {
        return list.stream().map(keyMapper).filter(Objects::nonNull).distinct().collect(Collectors.toList());
    }

    public static <T, R> List<R> map(Function<? super T, ? extends R> keyMapper, T ...arrays) {
        return map(Arrays.asList(arrays), keyMapper);
    }

    public static <T, R> Set<R> mapToSet(Collection<T> collection, Function<? super T, ? extends R> keyMapper) {
        return collection.stream().map(keyMapper).filter(Objects::nonNull).collect(Collectors.toSet());
    }

    /**
     * 判断两个列表及其内容是否一致
     */
    public static boolean equals(List l1, List l2) {
        if (Objects.equals(l1, l2)) {
            return true;
        }
        if (l1.size() != l2.size()) {
            return false;
        }
        for(int i = 0; i < l1.size(); i ++) {
            Object o1 = l1.get(i);
            Object o2 = l2.get(i);
            if (!o1.equals(o2)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断两个列表的元素是否一致，无关顺序
     */
    public static boolean equalsIgnoreOrder(List<?> list1, List<?> list2) {
        if (list1 == null || list2 == null) {
            return false;
        }
        if (list1.size() != list2.size()) {
            return false;
        }
        Map<Object, Integer> countMap = new HashMap<>();
        for (Object obj : list1) {
            countMap.put(obj, countMap.getOrDefault(obj, 0) + 1);
        }
        for (Object obj : list2) {
            Integer count = countMap.get(obj);
            if (count == null || count == 0) {
                return false;
            }
            countMap.put(obj, count - 1);
        }
        return true;
    }

    /**
     * 填充空值
     * @param list 原始列表
     * @param getDateFunction 获取日期的方法
     * @param function 获取填充物的方法
     * @param start 起始日期
     * @param end 结束日期
     * @return 填充后的列表
     */
    public static <T> List<T> fillVoids(List<? extends T> list, Function<? super T, LocalDate> getDateFunction, Function<LocalDate, ? extends T> function, LocalDate start, LocalDate end) {
        // 输入验证
        if (start.isAfter(end)) {
            throw new IllegalArgumentException("起始日期必须早于结束日期");
        }

        // 使用Map优化查找性能
        Map<String, T> fillAbleMap = new HashMap<>();
        for (T item : list) {
            fillAbleMap.put(DateUtils.getYYYY_MM_DD(getDateFunction.apply(item)), item);
        }

        // 构建结果列表，并确保线程安全
        List<T> result = Collections.synchronizedList(new ArrayList<>());
        LocalDate current = start;
        while (!current.isAfter(end)) {
            T currentItem = fillAbleMap.get(DateUtils.getYYYY_MM_DD(current));
            if (currentItem == null) {
                try {
                    currentItem = function.apply(current);
                } catch (Exception e) {
                    // 异常处理逻辑，例如记录日志或抛出自定义异常
                    System.err.println("处理日期 " + current + " 时发生异常: " + e.getMessage());
                }
            }
            if (currentItem != null) {
                result.add(currentItem);
            }
            current = current.plusDays(1);
        }
        return result;
    }

    /**
     * 将新旧列表转换为差异列表
     * @param newList 新列表
     * @param oldList 旧列表
     * @param primaryKeyFunc 唯一标识获取方法
     */
    public static<N extends B, O extends B, B> DiffListVO<N, O> convertToDiffList(List<N> newList, List<O> oldList, Function<B, Object> primaryKeyFunc) {
        DiffListVO<N, O> result = new DiffListVO<>();
        // 全新增
        if (CollectionUtils.isEmptyElement(oldList)) {
            return result.setAdd(newList);
        }
        // 全删除
        if (CollectionUtils.isEmptyElement(newList)) {
            return result.setDel(oldList);
        }

        Set<Object> newKeys = newList.stream().map(primaryKeyFunc).collect(Collectors.toSet());
        Set<Object> oldKeys = oldList.stream().map(primaryKeyFunc).collect(Collectors.toSet());

        // 不在旧列表则为新增
        List<N> addList = newList.stream().filter(item -> !oldKeys.contains(primaryKeyFunc.apply(item))).filter(Objects::nonNull).collect(Collectors.toList());
        result.setAdd(addList);
        // 不在新列表则为删除
        List<O> delList = oldList.stream().filter(item -> !newKeys.contains(primaryKeyFunc.apply(item))).filter(Objects::nonNull).collect(Collectors.toList());
        result.setDel(delList);
        // 在新列表，又在旧列表，则为更新
        List<N> updateList = newList.stream().filter(item -> oldKeys.contains(primaryKeyFunc.apply(item))).filter(Objects::nonNull).collect(Collectors.toList());
        result.setUpdate(updateList);

        return result;
    }

    
    /**
     * 计算集合中BigDecimal的和
     */
    public static <T> BigDecimal sumDecimal(Collection<T> collection, Function<T, BigDecimal> decimalMapper) {
        return collection.stream().map(decimalMapper).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}
