package org.springblade.thirdparty.oa.util;

import lombok.extern.slf4j.Slf4j;
import org.springblade.thirdparty.oa.constant.OAConstant;
import org.springblade.thirdparty.oa.pojo.response.OAResponse;
import org.springblade.thirdparty.oa.pojo.response.OAResponseData;
import org.springblade.thirdparty.oa.pojo.search.OAPageSearch;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 *  oa工具类
 * @author bfhuange
 * @date 2024/8/23
 */
@Slf4j
public class OAUtils {
    private OAUtils() {}

    /**
     * 将浮点型字符串转换成整数，字符串为空则转为1，否则截取字符串小数点前面的部门解析
     * @param doubleString 浮点类型字符串
     * @return
     */
    public static int parseInt(String doubleString) {
        if (doubleString == null || doubleString.isEmpty()) {
            return 1;
        }
        try {
            return Integer.parseInt(doubleString.split("\\.")[0]);
        } catch (Exception e) {
            log.error("解析数值字符串[{}]异常", doubleString, e);
            return 1;
        }
    }

    /**
     *  分页同步所有数据接口，返回所有数据无限制
     * @param param 接口分页参数
     * @param function 接口
     * @param exceptionFunction 异常方法
     * @return
     * @param <T>
     * @param <R>
     */
    public static <T extends OAPageSearch, R> List<R> pageSyncAll(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction) {
        return OAUtils.pageSync(param, function, exceptionFunction, Integer.MAX_VALUE);
    }

    /**
     *  分页同步数据接口
     * @param param 接口分页参数
     * @param function 接口
     * @param exceptionFunction 异常方法
     * @param threshold 阈值，当数据总数超过该值时，不再调用接口，返回数据
     * @return 参数是处理数据的方法的方法
     * @param <T>
     * @param <R>
     */
    public static <T extends OAPageSearch, R> Consumer<Consumer<List<R>>> pageSyncHandler(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold) {
        return OAUtils.pageSyncHandler(param, function, exceptionFunction, threshold, log::info);
    }

    /**
     *  分页同步数据接口
     * @param param 接口分页参数
     * @param function 接口
     * @param exceptionFunction 异常方法
     * @param threshold 阈值，当数据总数超过该值时，不再调用接口，返回数据
     * @return 参数是处理数据的方法的方法
     * @param <T>
     * @param <R>
     */
    public static <T extends OAPageSearch, R> Consumer<Consumer<List<R>>> pageSyncHandler(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold, BiConsumer<String, Object[]> logMethod) {
        return consumer -> {
            int i = 0;
            while (true) {
                // 循环调用接口直到阈值或最后一页
                List<R> data = OAUtils.pageSync(param, function, exceptionFunction, threshold, logMethod);
                if (data.isEmpty()) {
                    // 数据为空，结束
                    return;
                }
                logMethod.accept("批量处理OA同步数据 第{}次，{}条", new Object[]{++i, data.size()});
                // 数据不为空，处理数据
                consumer.accept(data);
                if (data.size() < threshold) {
                    // 数据比阈值小，说明到最后一页了，结束
                    return;
                }
            }
        };
    }

    /**
     *  分页同步数据接口
     * @param param 接口分页参数
     * @param function 接口
     * @param exceptionFunction 异常方法
     * @param threshold 阈值，当数据总数超过该值时，不再调用接口，返回数据
     * @return
     * @param <T>
     * @param <R>
     */
    public static <T extends OAPageSearch, R> List<R> pageSync(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold) {
        return OAUtils.pageSync(param, function, exceptionFunction, threshold, log::info);
    }

    /**
     *  分页同步数据接口
     * @param param 接口分页参数
     * @param function 接口
     * @param exceptionFunction 异常方法
     * @param threshold 阈值，当数据总数超过该值时，不再调用接口，返回数据
     * @param logMethod 日志输出
     * @return
     * @param <T>
     * @param <R>
     */
    public static <T extends OAPageSearch, R> List<R> pageSync(T param, Function<T, OAResponse<R>> function, Function<OAResponse<R>, RuntimeException> exceptionFunction, int threshold, BiConsumer<String, Object[]> logMethod) {
        long totalPage = -1;
        List<R> result = new ArrayList<>();
        do {
            // 调用接口
            OAResponse<R> oaResponse = function.apply(param);
            if (oaResponse== null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
                // 状态不是成功，抛出异常
                throw exceptionFunction.apply(oaResponse);
            }
            OAResponseData<R> data = oaResponse.getData();
            if (!CollectionUtils.isEmpty(data.getDataList())) {
                result.addAll(data.getDataList());
            }
            // 计算总页数
            totalPage = calcTotalPage(data);
            logMethod.accept("OA分页同步数据完成 {}/{}页", new Object[]{param.getCurPage(), totalPage});
            // 当前页 + 1
            param.setCurPage(param.getCurPage() + 1);
            // 数据量小于阈值且当前页（下一次）小于等于总页数，循环调用接口
        } while (result.size() < threshold && param.getCurPage() <= totalPage);
        return result;
    }

    /**
     *  计算总页数
     * @param data
     * @return
     * @param <R>
     */
    private static <R> long calcTotalPage(OAResponseData<R> data) {
        // 总数
        Long totalSize = data.getTotalSize();
        // 每页大小
        Integer pageSize = data.getPageSize();
        // 总页数 = 总数 / 每页大小，这里会舍去余数
        long totalPage = totalSize / pageSize;
        if (totalSize % pageSize != 0) {
            // 有余数总页数 +1
            totalPage += 1;
        }
        return totalPage;
    }
}
