package shop.alien.store.util;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.util.CollectionUtils;
import shop.alien.entity.store.LifeUser;
import shop.alien.entity.store.StoreUser;
import shop.alien.entity.store.dto.NearMeDto;
import shop.alien.util.common.constant.Constant;

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

/**
 * Magic wand
 */
public class FunctionMagic {

    /**
     * Magic for
     *
     * @param list
     * @param processor
     * @param <T>
     */
    public static <T> void processList(List<T> list, Consumer<T> processor) {
        Optional.ofNullable(list).ifPresent(l -> l.forEach(processor));
    }


    /**
     * Magic default for
     *
     * @param list
     * @param processor
     * @param defaultSupplier
     * @param <T>
     */

    public static <T> void processList(List<T> list, Consumer<T> processor, Supplier<List<T>> defaultSupplier) {
        List<T> target = CollectionUtils.isEmpty(list) ? defaultSupplier.get() : list;
        target.forEach(processor);
    }


    /**
     * Magic for GeoResult
     *
     * @param targetList
     * @param <T>
     * @return
     */
    public static <T> Consumer<GeoResult<RedisGeoCommands.GeoLocation<T>>> createGeoMagic(List<T> targetList) {
        return geoResult ->
                Optional.ofNullable(geoResult).
                        map(GeoResult::getContent).
                        map(RedisGeoCommands.GeoLocation::getName).
                        ifPresent(targetList::add);
    }


    /**
     * Magic for NearMeDto
     *
     * @param targetList
     * @param
     * @return
     */
    public static Consumer<GeoResult<RedisGeoCommands.GeoLocation<String>>> createNearMeDtoConsumer(List<NearMeDto> targetList) {
        return geoResult -> Optional.ofNullable(geoResult).
                map(GeoResult::getContent).
                ifPresent(content -> {
                    String name = content.getName();
                    Distance distance = geoResult.getDistance();
                    if (isNonNull(name).test(distance)) targetList.add(new NearMeDto(name, distance.getValue()));
                });
    }

    /**
     * Magic isNonNull
     *
     * @param var2
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> Predicate<T> isNonNull(R var2) {
        return var1 -> Objects.nonNull(var1) && Objects.nonNull(var2);
    }


    /**
     * Magic isEmptyMagic
     *
     * @param valueToCheck
     * @param columnExtractor
     * @param queryExecutor
     * @param <T>
     * @param <R>
     * @param <V>
     * @return
     */
    public static <T, R, V> boolean isEmptyMagic(
            V valueToCheck,
            SFunction<T, R> columnExtractor,
            SFunction<LambdaQueryWrapper<T>, T> queryExecutor) {

        if (valueToCheck == null) return true;

        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(columnExtractor);
        wrapper.eq(columnExtractor, valueToCheck);
        wrapper.last("LIMIT 1");

        T entity = queryExecutor.apply(wrapper);
        R columnValue = Optional.ofNullable(entity).map(columnExtractor).orElse(null);
        return columnValue != null;
    }


    /**
     * Magic buildLikeConditions
     *
     * @param wrapper
     * @param conditions
     * @param <T>
     */
    public static <T> void buildLikeConditions(
            LambdaQueryWrapper<T> wrapper,
            List<Triple<Boolean, SFunction<T, ?>, Object>> conditions) {
        conditions.forEach(condition -> wrapper.like(condition.getLeft(), condition.getMiddle(), condition.getRight()));
    }

    /**
     * Magic convertToIds
     *
     * @param list
     * @param idExtractor
     * @param <T>
     * @return
     */
    public static <T> List<String> convertToIds(
            List<T> list,
            Function<T, ?> idExtractor) {
        return Optional.ofNullable(list).
                orElse(Collections.emptyList()).
                stream().map(idExtractor).
                filter(Objects::nonNull).
                map(Object::toString).
                collect(Collectors.toList());
    }

    /**
     * Magic isListFlag
     *
     * @param list
     * @param list1
     * @param <T>
     * @return
     */

    public static <T> boolean isListFlag(
            List<T> list,
            List<T> list1
    ) {
        return !(Objects.equals(list.get(0), "") && Objects.equals(list1.get(0), ""));

    }

    /**
     * Magic populateUserInfo
     *
     * @param dto
     * @param userType
     * @param userId
     * @param userQuery
     * @param nameSetter
     * @param phoneSetter
     * @param <T>
     * @param <D>
     */
    public static <T, D> void populateUserInfo(
            D dto,
            String userType,
            String userId,
            Function<String, T> userQuery,
            BiConsumer<D, String> nameSetter,
            BiConsumer<D, String> phoneSetter) {

        T user = Optional.ofNullable(userId)
                .map(userQuery)
                .orElseThrow(() -> new RuntimeException("User not found"));

        if ("1".equals(userType)) {
            // M类型处理逻辑
            if (user instanceof StoreUser) {
                StoreUser storeUser = (StoreUser) user;
                nameSetter.accept(dto, storeUser.getName());
                phoneSetter.accept(dto, storeUser.getPhone());
            }
        } else {
            // U类型处理逻辑
            if (user instanceof LifeUser) {
                LifeUser lifeUser = (LifeUser) user;
                nameSetter.accept(dto, lifeUser.getUserName());
                phoneSetter.accept(dto, lifeUser.getUserPhone());
            }
        }
    }


    /**
     * Magic  handleUserInfo
     *
     * @param userType
     * @param userId
     * @param storeQuery
     * @param lifeQuery
     * @param consumer
     * @param <T>
     */

    public static <T> void handleUserInfo(
            String userType,
            String userId,
            Function<String, T> storeQuery,
            Function<String, T> lifeQuery,
            Consumer<T> consumer) {
        if (userId == null) return;
        T user = Constant.SEQUENCE_1.equals(userType) ?
                storeQuery.apply(userId) :
                lifeQuery.apply(userId);
        if (user != null) {
            consumer.accept(user);
        }
    }


    /**
     * Magic idQueryWrapper
     *
     * @param userId
     * @param <T>
     * @return
     */
    public static <T> QueryWrapper<T> idQueryWrapper(String userId) {
        return new QueryWrapper<T>().eq("id", userId).last("OR (delete_flag = 1 AND id = " + userId + ") limit 1"); // 强制包含已删除的记录;
    }


    // 举报内容
    private static final Map<String, String> CONTEXT = new HashMap<>();

    static {
        CONTEXT.put("1", "用户");
        CONTEXT.put("2", "动态");
        CONTEXT.put("3", "评论");
        CONTEXT.put("4", "二手商品");
        CONTEXT.put("5", "二手用户");
    }


    // 举报理由
    private static final Map<String, String> VIOLATION = new HashMap<>();

    static {
        VIOLATION.put("1", "用户违规");
        VIOLATION.put("2", "色情低俗");
        VIOLATION.put("3", "违法违规");
        VIOLATION.put("4", "谩骂嘲讽、煽动对立");
        VIOLATION.put("5", "涉嫌诈骗");
        VIOLATION.put("6", "人身攻击");
        VIOLATION.put("7", "种族歧视");
        VIOLATION.put("8", "政治敏感");
        VIOLATION.put("9", "虚假、不实内容、违反公德秩序");
        VIOLATION.put("10", "违反公德秩序");
        VIOLATION.put("11", "危害人身安全");
        VIOLATION.put("12", "网络暴力");
        VIOLATION.put("13", "其他原因");
    }


    // 状态
    private static final Map<String, String> STATUS = new HashMap<>();

    static {
        STATUS.put("0", "待处理");
        STATUS.put("1", "已通过");
        STATUS.put("2", "已驳回");
    }


    public static String getByCode(String statusCode, Map<String, String> mapping) {
        return Optional.ofNullable(mapping.get(statusCode))
                .orElseThrow(() -> new IllegalArgumentException("非法状态码: " + statusCode));
    }


    // 举报内容
    public static String getContext(String statusCode) {
        return getByCode(statusCode, CONTEXT);
    }

    // 举报理由
    public static String violation(String statusCode) {
        return getByCode(statusCode, VIOLATION);
    }


    // 状态
    public static String status(String statusCode) {
        return getByCode(statusCode, STATUS);
    }


}
