package egovframework.util;

import org.springframework.stereotype.Component;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 컬렉션 관련 공통 유틸리티 클래스
 */
@Component
public class CollectionUtil {

    /**
     * 컬렉션이 null이거나 비어있는지 확인
     * @param collection 검사할 컬렉션
     * @return null이거나 비어있으면 true, 그렇지 않으면 false
     */
    public static boolean isEmpty(Collection<?> collection) {
        return collection == null || collection.isEmpty();
    }

    /**
     * 컬렉션이 null이 아니고 비어있지 않은지 확인
     * @param collection 검사할 컬렉션
     * @return null이 아니고 비어있지 않으면 true, 그렇지 않으면 false
     */
    public static boolean isNotEmpty(Collection<?> collection) {
        return !isEmpty(collection);
    }

    /**
     * 맵이 null이거나 비어있는지 확인
     * @param map 검사할 맵
     * @return null이거나 비어있으면 true, 그렇지 않으면 false
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    /**
     * 맵이 null이 아니고 비어있지 않은지 확인
     * @param map 검사할 맵
     * @return null이 아니고 비어있지 않으면 true, 그렇지 않으면 false
     */
    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }

    /**
     * 배열이 null이거나 비어있는지 확인
     * @param array 검사할 배열
     * @return null이거나 비어있으면 true, 그렇지 않으면 false
     */
    public static <T> boolean isEmpty(T[] array) {
        return array == null || array.length == 0;
    }

    /**
     * 배열이 null이 아니고 비어있지 않은지 확인
     * @param array 검사할 배열
     * @return null이 아니고 비어있지 않으면 true, 그렇지 않으면 false
     */
    public static <T> boolean isNotEmpty(T[] array) {
        return !isEmpty(array);
    }

    /**
     * 컬렉션의 크기 반환 (null 안전)
     * @param collection 검사할 컬렉션
     * @return 컬렉션의 크기, null이면 0 반환
     */
    public static int size(Collection<?> collection) {
        return collection == null ? 0 : collection.size();
    }

    /**
     * 맵의 크기 반환 (null 안전)
     * @param map 검사할 맵
     * @return 맵의 크기, null이면 0 반환
     */
    public static int size(Map<?, ?> map) {
        return map == null ? 0 : map.size();
    }

    /**
     * 배열의 크기 반환 (null 안전)
     * @param array 검사할 배열
     * @return 배열의 크기, null이면 0 반환
     */
    public static <T> int size(T[] array) {
        return array == null ? 0 : array.length;
    }

    /**
     * 컬렉션이 null이면 빈 컬렉션 반환, 그렇지 않으면 원래 컬렉션 반환
     * @param collection 처리할 컬렉션
     * @return null이면 빈 컬렉션, 그렇지 않으면 원래 컬렉션
     */
    public static <T> Collection<T> emptyIfNull(Collection<T> collection) {
        return collection == null ? Collections.emptyList() : collection;
    }

    /**
     * 리스트가 null이면 빈 리스트 반환, 그렇지 않으면 원래 리스트 반환
     * @param list 처리할 리스트
     * @return null이면 빈 리스트, 그렇지 않으면 원래 리스트
     */
    public static <T> List<T> emptyIfNull(List<T> list) {
        return list == null ? Collections.emptyList() : list;
    }

    /**
     * 맵이 null이면 빈 맵 반환, 그렇지 않으면 원래 맵 반환
     * @param map 처리할 맵
     * @return null이면 빈 맵, 그렇지 않으면 원래 맵
     */
    public static <K, V> Map<K, V> emptyIfNull(Map<K, V> map) {
        return map == null ? Collections.emptyMap() : map;
    }

    /**
     * 배열을 리스트로 변환 (null 안전)
     * @param array 변환할 배열
     * @return 변환된 리스트, null이면 빈 리스트 반환
     */
    public static <T> List<T> toList(T[] array) {
        if (array == null) {
            return Collections.emptyList();
        }
        return Arrays.asList(array);
    }

    /**
     * 컬렉션을 리스트로 변환 (null 안전)
     * @param collection 변환할 컬렉션
     * @return 변환된 리스트, null이면 빈 리스트 반환
     */
    public static <T> List<T> toList(Collection<T> collection) {
        if (collection == null) {
            return Collections.emptyList();
        }
        return new ArrayList<>(collection);
    }

    /**
     * 컬렉션을 Set으로 변환 (null 안전)
     * @param collection 변환할 컬렉션
     * @return 변환된 Set, null이면 빈 Set 반환
     */
    public static <T> Set<T> toSet(Collection<T> collection) {
        if (collection == null) {
            return Collections.emptySet();
        }
        return new HashSet<>(collection);
    }

    /**
     * 배열을 Set으로 변환 (null 안전)
     * @param array 변환할 배열
     * @return 변환된 Set, null이면 빈 Set 반환
     */
    public static <T> Set<T> toSet(T[] array) {
        if (array == null) {
            return Collections.emptySet();
        }
        Set<T> set = new HashSet<>();
        Collections.addAll(set, array);
        return set;
    }

    /**
     * 컬렉션에서 조건에 맞는 요소만 필터링
     * @param collection 필터링할 컬렉션
     * @param predicate 필터링 조건
     * @return 필터링된 리스트
     */
    public static <T> List<T> filter(Collection<T> collection, Predicate<T> predicate) {
        if (isEmpty(collection)) {
            return Collections.emptyList();
        }
        return collection.stream()
                .filter(predicate)
                .collect(Collectors.toList());
    }

    /**
     * 컬렉션의 요소를 변환
     * @param collection 변환할 컬렉션
     * @param mapper 변환 함수
     * @return 변환된 리스트
     */
    public static <T, R> List<R> map(Collection<T> collection, Function<T, R> mapper) {
        if (isEmpty(collection)) {
            return Collections.emptyList();
        }
        return collection.stream()
                .map(mapper)
                .collect(Collectors.toList());
    }

    /**
     * 컬렉션을 정렬
     * @param collection 정렬할 컬렉션
     * @param comparator 정렬 기준
     * @return 정렬된 리스트
     */
    public static <T> List<T> sort(Collection<T> collection, Comparator<? super T> comparator) {
        if (isEmpty(collection)) {
            return Collections.emptyList();
        }
        List<T> list = new ArrayList<>(collection);
        list.sort(comparator);
        return list;
    }

    /**
     * 컬렉션을 자연 순서로 정렬 (Comparable 구현 필요)
     * @param collection 정렬할 컬렉션
     * @return 정렬된 리스트
     */
    public static <T extends Comparable<? super T>> List<T> sort(Collection<T> collection) {
        if (isEmpty(collection)) {
            return Collections.emptyList();
        }
        List<T> list = new ArrayList<>(collection);
        Collections.sort(list);
        return list;
    }

    /**
     * 컬렉션에서 중복 제거
     * @param collection 중복 제거할 컬렉션
     * @return 중복이 제거된 리스트
     */
    public static <T> List<T> distinct(Collection<T> collection) {
        if (isEmpty(collection)) {
            return Collections.emptyList();
        }
        return collection.stream()
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 컬렉션을 특정 키를 기준으로 그룹화
     * @param collection 그룹화할 컬렉션
     * @param keyMapper 그룹화 키 추출 함수
     * @return 그룹화된 맵
     */
    public static <T, K> Map<K, List<T>> groupBy(Collection<T> collection, Function<T, K> keyMapper) {
        if (isEmpty(collection)) {
            return Collections.emptyMap();
        }
        return collection.stream()
                .collect(Collectors.groupingBy(keyMapper));
    }

    /**
     * 컬렉션을 특정 키를 기준으로 맵으로 변환
     * @param collection 변환할 컬렉션
     * @param keyMapper 키 추출 함수
     * @return 변환된 맵
     */
    public static <T, K> Map<K, T> toMap(Collection<T> collection, Function<T, K> keyMapper) {
        if (isEmpty(collection)) {
            return Collections.emptyMap();
        }
        return collection.stream()
                .collect(Collectors.toMap(keyMapper, Function.identity(), (a, b) -> a));
    }

    /**
     * 컬렉션을 특정 키와 값을 기준으로 맵으로 변환
     * @param collection 변환할 컬렉션
     * @param keyMapper 키 추출 함수
     * @param valueMapper 값 추출 함수
     * @return 변환된 맵
     */
    public static <T, K, V> Map<K, V> toMap(Collection<T> collection, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        if (isEmpty(collection)) {
            return Collections.emptyMap();
        }
        return collection.stream()
                .collect(Collectors.toMap(keyMapper, valueMapper, (a, b) -> a));
    }

    /**
     * 두 컬렉션의 합집합 반환
     * @param collection1 첫 번째 컬렉션
     * @param collection2 두 번째 컬렉션
     * @return 합집합 리스트
     */
    public static <T> List<T> union(Collection<T> collection1, Collection<T> collection2) {
        Set<T> set = new HashSet<>();
        if (isNotEmpty(collection1)) {
            set.addAll(collection1);
        }
        if (isNotEmpty(collection2)) {
            set.addAll(collection2);
        }
        return new ArrayList<>(set);
    }

    /**
     * 두 컬렉션의 교집합 반환
     * @param collection1 첫 번째 컬렉션
     * @param collection2 두 번째 컬렉션
     * @return 교집합 리스트
     */
    public static <T> List<T> intersection(Collection<T> collection1, Collection<T> collection2) {
        if (isEmpty(collection1) || isEmpty(collection2)) {
            return Collections.emptyList();
        }

        List<T> result = new ArrayList<>();
        Set<T> set = new HashSet<>(collection2);

        for (T item : collection1) {
            if (set.contains(item)) {
                result.add(item);
            }
        }

        return result;
    }

    /**
     * 두 컬렉션의 차집합 반환 (collection1 - collection2)
     * @param collection1 첫 번째 컬렉션
     * @param collection2 두 번째 컬렉션
     * @return 차집합 리스트
     */
    public static <T> List<T> difference(Collection<T> collection1, Collection<T> collection2) {
        if (isEmpty(collection1)) {
            return Collections.emptyList();
        }
        if (isEmpty(collection2)) {
            return new ArrayList<>(collection1);
        }

        List<T> result = new ArrayList<>(collection1);
        result.removeAll(new HashSet<>(collection2));
        return result;
    }

    /**
     * 컬렉션을 지정된 크기의 하위 리스트로 분할
     * @param collection 분할할 컬렉션
     * @param size 하위 리스트의 크기
     * @return 분할된 하위 리스트의 리스트
     */
    public static <T> List<List<T>> partition(Collection<T> collection, int size) {
        if (isEmpty(collection)) {
            return Collections.emptyList();
        }
        if (size <= 0) {
            throw new IllegalArgumentException("Size must be greater than 0");
        }

        List<List<T>> result = new ArrayList<>();
        List<T> list = new ArrayList<>(collection);
        int total = list.size();

        for (int i = 0; i < total; i += size) {
            result.add(list.subList(i, Math.min(total, i + size)));
        }

        return result;
    }

    /**
     * 컬렉션에서 첫 번째 요소 반환 (null 안전)
     * @param collection 처리할 컬렉션
     * @return 첫 번째 요소, 없으면 null 반환
     */
    public static <T> T getFirst(Collection<T> collection) {
        if (isEmpty(collection)) {
            return null;
        }
        return collection.iterator().next();
    }

    /**
     * 리스트에서 마지막 요소 반환 (null 안전)
     * @param list 처리할 리스트
     * @return 마지막 요소, 없으면 null 반환
     */
    public static <T> T getLast(List<T> list) {
        if (isEmpty(list)) {
            return null;
        }
        return list.get(list.size() - 1);
    }

    /**
     * 맵에서 키에 해당하는 값 반환 (null 안전)
     * @param map 처리할 맵
     * @param key 키
     * @param defaultValue 기본값
     * @return 키에 해당하는 값, 없으면 기본값 반환
     */
    public static <K, V> V getOrDefault(Map<K, V> map, K key, V defaultValue) {
        if (isEmpty(map)) {
            return defaultValue;
        }
        return map.getOrDefault(key, defaultValue);
    }

    /**
     * 두 맵을 병합
     * @param map1 첫 번째 맵
     * @param map2 두 번째 맵
     * @return 병합된 맵
     */
    public static <K, V> Map<K, V> merge(Map<K, V> map1, Map<K, V> map2) {
        Map<K, V> result = new HashMap<>();

        if (isNotEmpty(map1)) {
            result.putAll(map1);
        }

        if (isNotEmpty(map2)) {
            result.putAll(map2);
        }

        return result;
    }
}
