package com.iformall.service.invest;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import com.iformall.common.ErrorCode;
import com.iformall.domain.po.invest.InvestBaseEntity;
import com.iformall.domain.vo.invest.InvestPageQuery;
import com.iformall.exception.MallinkException;
import org.apache.commons.compress.utils.Lists;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Collection;
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;

public class InvestHelper {

	public static <T> Page<T> buildQueryPage(InvestPageQuery<T> pageQuery) {
		Page<T> page = new Page<>();
		page.setCurrent(pageQuery.getPageNum());
		page.setSize(pageQuery.getPageSize());
		page.setAscs(camelToUnderline(pageQuery.getAscs()));
		page.setDescs(camelToUnderline(pageQuery.getDescs()));
		page.setOptimizeCountSql(true);
		page.setSearchCount(true);
		return page;
	}

	private static List<String> camelToUnderline(List<String> orders) {
		if (CollectionUtils.isEmpty(orders)) {
			return orders;
		}

		List<String> newOrders = Lists.newArrayList();
		for (String order : orders) {
			newOrders.add(StringUtils.camelToUnderline(order));
		}
		return newOrders;
	}

	/**
	 * @param objects the object to check
	 * @throws MallinkException if the object is {@code null}
	 */
	public static void notNull(final Object... objects) {
		if (objects == null) {
			throwException(ErrorCode.INVEST_PARAMETER_NOT_FOUND, "参数");
		}

		for (final Object val : objects) {
			if (val == null) {
				throwException(ErrorCode.INVEST_PARAMETER_NOT_FOUND, "参数");
			}
		}
	}

	/**
	 * @param object  the object to check
	 * @param message the exception message to use if the assertion fails
	 * @throws MallinkException if the object is {@code null}
	 */
	public static void notNull(Object object, String message) {
		if (object == null) {
			throwException(ErrorCode.INVEST_PARAMETER_NOT_FOUND, message);
		}
	}

	/**
	 * @param object    the object to check
	 * @param errorCode the exception message to use if the assertion fails
	 * @throws MallinkException if the object is {@code null}
	 */
	public static void notNull(Object object, ErrorCode errorCode) {
		if (object == null) {
			throwException(errorCode, null);
		}
	}

	/**
	 * @param expression
	 * @param errorCode
	 */
	public static void isTrue(boolean expression, ErrorCode errorCode, Object info) {
		if (!expression) {
			throwException(errorCode, info);
		}
	}

	public static void throwException(ErrorCode errorCode, Object info) {
		String message;
		if (Objects.isNull(info)) {
			message = errorCode.getMessage();
		} else {
			message = MessageFormat.format("{0} {1}", info, errorCode.getMessage());
		}
		throw new MallinkException(errorCode.getCode(), message);
	}

	/**
	 *
	 * @param value the object to check
	 * @param message the exception message to use if the assertion fails
	 */
	public static void checkResult(Object value, String message) {
		if (Objects.isNull(value)) {
			throwException(ErrorCode.INVEST_RESULT_NOT_FOUND, message);
		}
	}

	public static <T extends Enum<T>> int code(T t) {
		try {
			Method code = t.getClass().getDeclaredMethod("code");
			Object rs = code.invoke(t);
			return (int) rs;
		} catch (ReflectiveOperationException e) {
			throw new IllegalArgumentException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public static <T extends Enum<T>> T codeOf(Class enumType, String value) {
		if (StringUtils.isEmpty(value)) {
			return null;
		}
		int code = Integer.parseInt(value);
		try {
			Method method = enumType.getDeclaredMethod("getEnum", Integer.class);
			Object rs = method.invoke(null, code);
			return (T) rs;
		} catch (ReflectiveOperationException e) {
			throw new IllegalArgumentException(e);
		}
	}

	public static String getEnumVal(Object o) {
		try {
			Method method = o.getClass().getDeclaredMethod("getInfo");
			Object rs = method.invoke(o);
			return (String) rs;
		} catch (ReflectiveOperationException e) {
			throw new IllegalArgumentException(e);
		}
	}

	public static <T extends Enum<T>> T valueOf(Class enumType, String name) {
		return (T) T.valueOf(enumType, name);
	}

	public  <T, R> ImmutableListMultimap<R, T> getMultiMap(Collection<T> dataList) {
		return Multimaps.index(dataList, new com.google.common.base.Function<T, R>() {
			@Nullable
			@Override
			public R apply(@Nullable T input) {
				if (input instanceof InvestBaseEntity) {
					return (R) ((InvestBaseEntity) input).getId();
				}
				return null;
			}
		});
	}

	public static  <T, R> Map<R, T> getMap(Collection<T> dataList, Function<T, R> keyMapper) {
		return dataList.parallelStream().collect(Collectors.toMap(keyMapper, entity -> entity));
	}

	public static <T, R> Collection<R> getIds(Collection<T> dataList, Function<? super T, R> mapper) {
		return dataList.parallelStream().map(mapper).collect(Collectors.toList());
	}

	public static <T, R> Collection<R> getIds(Collection<T> dataList, Predicate<T> predicate,Function<? super T, R> mapper) {
		return dataList.parallelStream().filter(predicate).map(mapper).collect(Collectors.toList());
	}
}
