package com.ruoyi.common.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class ContainerUtils
{
    public static <T, R> List<T> unique(Function<T, R> getIdFunc, Collection<T>...args)
    {
        List<T> list = new ArrayList<>();
        Arrays.stream(args).filter(CollectionUtil::isNotEmpty).forEach(list::addAll);
        return unique(list, getIdFunc);
    }

    public static <T, R> List<T> unique_override(Function<T, R> getIdFunc, Collection<T>...args)
    {
        List<T> list = new ArrayList<>();
        Arrays.stream(args).filter(CollectionUtil::isNotEmpty).forEach(list::addAll);
        return unique_override(list, getIdFunc);
    }

    // keep
    public static <T, R> List<T> unique(Collection<T> collection, Function<T, R> getIdFunc)
    {
        return new ArrayList<>(collection.stream().collect(Collectors.toMap(getIdFunc, x -> x, (a, b) -> a, LinkedHashMap::new)).values());
    }

    public static <T, R> List<T> unique_override(Collection<T> collection, Function<T, R> getIdFunc)
    {
        return new ArrayList<>(collection.stream().collect(Collectors.toMap(getIdFunc, x -> x, (a, b) -> b, LinkedHashMap::new)).values());
    }

    public static <T, R> List<R> mapToList(Collection<T> collection, Function<T, R> mapFunc, boolean...unique)
    {
        Stream<R> rStream = collection.stream().map(mapFunc);
        if(null != unique && unique.length > 0 && ConvertUtils.FALSE(unique[0]))
            rStream = rStream.distinct();
        return rStream.collect(Collectors.toList());
    }

    public static <T> List<T> subList(List<T> list, int start, Integer...end)
    {
        int num = list.size();
        if(num == 0)
            return new ArrayList<>();
        if(start < 0)
            start = num + start;
        if(start < 0 || start >= num)
            return new ArrayList<>();
        int e = list.size();
        if(null != end && end.length > 0)
            e = end[0];
        if(e < 0)
            e = list.size() + e;
        if(e < 0 || e > num)
            return new ArrayList<>();
        return list.subList(start, e);
    }

    public static <T> List<T> filterToList(Collection<T> collection, Predicate<T> filterFunc)
    {
        return collection.stream().filter(filterFunc).collect(Collectors.toList());
    }

    public static <T, R> List<R> mapToUniqueList(Collection<T> collection, Function<T, R> mapFunc)
    {
        return collection.stream().map(mapFunc).distinct().collect(Collectors.toList());
    }

    public static <T, R> Map<R, List<T>> groupingBy(Collection<T> collection, Function<T, R> mapFunc)
    {
        return collection.stream().collect(Collectors.groupingBy(mapFunc));
    }

    public static <T, R, U> Map<R, List<U>> groupingBy(Collection<T> collection, Function<T, R> mapFunc, Function<T, U> mapValueFunc)
    {
        return collection.stream().collect(Collectors.groupingBy(mapFunc, Collectors.mapping(mapValueFunc, Collectors.toList())));
    }

    // keep
    public static <T, R> Map<R, T> toMap(Collection<T> collection, Function<T, R> mapFunc)
    {
        return collection.stream().collect(Collectors.toMap(mapFunc, x -> x, (a, b) -> a));
    }

    public static <T, R> Map<R, T> toMap_override(Collection<T> collection, Function<T, R> mapFunc)
    {
        return collection.stream().collect(Collectors.toMap(mapFunc, x -> x, (a, b) -> b));
    }

    // keep
    public static <T, R, U> Map<R, U> toMap(Collection<T> collection, Function<T, R> mapFunc, Function<T, U> valFunc)
    {
        return collection.stream().collect(Collectors.toMap(mapFunc, valFunc, (a, b) -> a));
    }

    public static <T, R, U> Map<R, U> toMap_override(Collection<T> collection, Function<T, R> mapFunc, Function<T, U> valFunc)
    {
        return collection.stream().collect(Collectors.toMap(mapFunc, valFunc, (a, b) -> b));
    }

    // keep
    public static <T, R> Map<R, T> toLinkedMap(Collection<T> collection, Function<T, R> mapFunc)
    {
        return collection.stream().collect(Collectors.toMap(mapFunc, x -> x, (a, b) -> a, LinkedHashMap::new));
    }

    public static <T, R> Map<R, T> toLinkedMap_override(Collection<T> collection, Function<T, R> mapFunc)
    {
        return collection.stream().collect(Collectors.toMap(mapFunc, x -> x, (a, b) -> b, LinkedHashMap::new));
    }
/*
    *//**
     * ListTriple::negativeList -> 集合a独有的差集
     * ListTriple::zeroList -> 集合a和集合b共有的交集
     * ListTriple::positiveList -> 集合b独有的差集
     *//*
    public static <T> ListTriple<T> intersection_difference(List<T> a, List<T> b, boolean...distinct *//* = false: 差集去重 *//*)
    {
        if(CollectionUtil.isEmpty(a) && CollectionUtil.isEmpty(b))
            return ListTriple.empty_triple();
        if(CollectionUtil.isEmpty(a) && CollectionUtil.isNotEmpty(b))
            return ListTriple.make_triple(b, (x) -> 1);
        if(CollectionUtil.isNotEmpty(a) && CollectionUtil.isEmpty(b))
            return ListTriple.make_triple(a, (x) -> -1);
        ListTriple<T> triple = ListTriple.empty_triple();
        List<T> intersection = a.stream().filter(b::contains).distinct().collect(Collectors.toList());
        triple.zeroList.addAll(intersection);
        if(null != distinct && distinct.length > 0 && distinct[0])
        {
            triple.negativeList.addAll(a.stream().filter((x) -> !intersection.contains(x)).distinct().collect(Collectors.toList()));
            triple.positiveList.addAll(b.stream().filter((x) -> !intersection.contains(x)).distinct().collect(Collectors.toList()));
        }
        else
        {
            triple.negativeList.addAll(a.stream().filter((x) -> !intersection.contains(x)).collect(Collectors.toList()));
            triple.positiveList.addAll(b.stream().filter((x) -> !intersection.contains(x)).collect(Collectors.toList()));
        }
        return triple;
    }*/

    public static <T> T get(Collection<T> list, int index)
    {
        if(CollectionUtil.isEmpty(list))
            return null;
        int size = list.size();
        int i = index < 0 ? size + index : index;
        if(i < 0 || i >= size)
            return null;
        if(list instanceof List)
            return ((List<T>) list).get(i);
        else
        {
            Iterator<T> itor = list.iterator();
            int c = 0;
            while(itor.hasNext())
            {
                T val = itor.next();
                if(c == i)
                    return val;
                c++;
            }
        }
        return null;
    }

    public static <T> boolean contains_ptr(Collection<T> list, T target)
    {
        if(CollectionUtil.isEmpty(list))
            return false;
        return list.stream().anyMatch((x) -> x == target);
    }

    public static <T> int indexOf_ptr(List<T> list, T target)
    {
        if(CollectionUtil.isEmpty(list))
            return -1;
        for(int i = 0; i < list.size(); i++)
        {
            if(list.get(i) == target)
                return i;
        }
        return -1;
    }

    public static <T> List<T> combine(Collection<T>...src)
    {
        if(null != src && src.length > 0)
        {
            return Arrays.stream(src)
                  .filter(Objects::nonNull)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toList());
        }

        return new ArrayList<>();
    }

    public static <T> List<T> combine(List<List<T>> src)
    {
        if(CollectionUtil.isNotEmpty(src))
        {
            return src.stream()
                         .filter(Objects::nonNull)
                         .flatMap(Collection::stream)
                         .collect(Collectors.toList());
        }

        return new ArrayList<>();
    }

    public static <T> BigDecimal reduce_s(Collection<T> list, Function<T, BigDecimal> getDecimalFunc, BigDecimal initValue)
    {
        BigDecimal init = null != initValue ? initValue : BigDecimal.ZERO;
        if(CollectionUtil.isEmpty(list))
            return init;
        return list.stream().map(getDecimalFunc).reduce(init, NumberUtil::add);
    }

    public static <T> BigDecimal reduce_s(Collection<T> list, Function<T, BigDecimal> getDecimalFunc)
    {
        return reduce_s(list, getDecimalFunc, BigDecimal.ZERO);
    }

    private ContainerUtils() {}
}
