package com.lanyuanxiaoyao.service.hudi.utils;

import cn.hutool.core.util.ReUtil;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hudi.common.table.HoodieTableMetaClient;
import org.apache.hudi.common.table.TableSchemaResolver;
import org.apache.hudi.common.table.timeline.HoodieDefaultTimeline;
import org.apache.hudi.common.table.timeline.HoodieInstant;
import org.apache.hudi.common.table.timeline.HoodieTimeline;
import org.apache.hudi.org.apache.avro.Schema;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.list.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Hudi 工具
 *
 * @author lanyuanxiaoyao
 * @date 2023-05-01
 */
public class HoodieUtils {
    private static final Logger logger = LoggerFactory.getLogger(HoodieUtils.class);

    public static Schema getSchema(String hdfs) throws Exception {
        HoodieTableMetaClient client = HoodieTableMetaClient.builder()
                .setConf(new Configuration())
                .setBasePath(hdfs)
                .build();
        return getSchema(client);
    }

    public static Schema getSchema(HoodieTableMetaClient client) throws Exception {
        TableSchemaResolver schemaUtil = new TableSchemaResolver(client);
        return schemaUtil.getTableAvroSchema(true);
    }

    public static ImmutableList<HoodieInstant> getAllInstants(HoodieTableMetaClient client, Function<HoodieTableMetaClient, HoodieDefaultTimeline> getTimeline) throws IOException {
        FileSystem fileSystem = client.getRawFs();
        // 直接使用 toString 方法得到的值是被缓存的
        String hdfs = client.getBasePathV2().toUri().toString();
        Path metadataPath = new Path(hdfs + "/.hoodie");
        return getAllInstants(getTimeline.apply(client), fileSystem, metadataPath)
                .toSortedList(HoodieInstant::compareTo)
                .toImmutable();
    }

    private static ImmutableList<HoodieInstant> getAllInstants(HoodieDefaultTimeline timeline, FileSystem fileSystem, Path metadataPath) throws IOException {
        Set<String> committedTimestamps = timeline.getCommitsTimeline()
                .filterCompletedInstants()
                .getInstants()
                .map(HoodieInstant::getTimestamp)
                .collect(Collectors.toSet());
        List<String> compactionRequestedTimestamps = Arrays.stream(fileSystem.listStatus(metadataPath))
                .filter(status -> status.getPath().toString().endsWith(HoodieTimeline.REQUESTED_COMPACTION_EXTENSION))
                .map(status -> status.getPath().getName())
                .map(name -> ReUtil.get("^(\\d+)\\..+", name, 1))
                .filter(committedTimestamps::contains)
                .collect(Collectors.toList());
        return Lists.immutable.ofAll(timeline.getInstants()
                .map(instant -> {
                    if (compactionRequestedTimestamps.contains(instant.getTimestamp())) {
                        return new HoodieInstant(HoodieInstant.State.COMPLETED, HoodieTimeline.COMPACTION_ACTION, instant.getTimestamp());
                    }
                    return instant;
                })
                .sorted(Comparator.comparingLong(i -> Long.parseLong(i.getTimestamp())))
                .collect(Collectors.toList()));
    }
}

