package com.lanyuanxiaoyao.service.hudi.service;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import com.lanyuanxiaoyao.service.common.entity.TableMeta;
import com.lanyuanxiaoyao.service.configuration.entity.hudi.HPath;
import com.lanyuanxiaoyao.service.forest.service.InfoService;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.ContentSummary;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hudi.common.table.HoodieTableMetaClient;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author lanyuanxiaoyao
 * @date 2024-01-24
 */
@SuppressWarnings("SpringCacheableMethodCallsInspection")
@Service
public class HdfsService {
    private static final Logger logger = LoggerFactory.getLogger(HdfsService.class);

    private final InfoService infoService;

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    public HdfsService(InfoService infoService) {
        this.infoService = infoService;
    }

    @Cacheable(value = "exists-hudi-table", sync = true, key = "#flinkJobId.toString()+#alias")
    public Boolean existsHudiTable(Long flinkJobId, String alias) {
        try {
            TableMeta meta = infoService.tableMetaDetail(flinkJobId, alias);
            if (ObjectUtil.isNotNull(meta)) {
                return existsHudiTable(meta.getHudi().getTargetHdfsPath());
            }
        } catch (Throwable ignored) {
        }
        return false;
    }

    @Cacheable(value = "exists-hudi-table", sync = true)
    public Boolean existsHudiTable(String hdfs) {
        if (!existsPath(hdfs)) {
            return false;
        }
        try {
            HoodieTableMetaClient.builder()
                    .setConf(new Configuration())
                    .setBasePath(hdfs)
                    .build();
            return true;
        } catch (Throwable ignored) {
        }
        return false;
    }

    @Cacheable(value = "exists-path", sync = true)
    public Boolean existsPath(String hdfs) {
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            return fileSystem.exists(new Path(hdfs));
        } catch (IOException ignored) {
        }
        return false;
    }

    @Cacheable(value = "get-hpath", sync = true)
    public HPath get(String root) throws IOException {
        if (!existsPath(root)) {
            throw new RuntimeException("File not found");
        }
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            FileStatus status = fileSystem.getFileStatus(new Path(root));
            return new HPath(
                    status.getPath().getName(),
                    status.getPath().toString(),
                    status.isFile(),
                    status.isDirectory(),
                    status.getLen(),
                    status.getGroup(),
                    status.getOwner(),
                    status.getPermission().toString(),
                    (int) status.getReplication(),
                    status.getModificationTime()
            );
        }
    }

    @Cacheable(value = "list-hpath", sync = true)
    public ImmutableList<HPath> list(String root) throws IOException {
        if (!existsPath(root)) {
            return Lists.immutable.empty();
        }
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            MutableList<HPath> files = Lists.mutable.empty();
            for (FileStatus status : fileSystem.listStatus(new Path(root))) {
                files.add(new HPath(
                        status.getPath().getName(),
                        status.getPath().toString(),
                        status.isFile(),
                        status.isDirectory(),
                        status.getLen(),
                        status.getGroup(),
                        status.getOwner(),
                        status.getPermission().toString(),
                        (int) status.getReplication(),
                        status.getModificationTime()
                ));
            }
            return files.toImmutable();
        }
    }

    @Cacheable(value = "read-hpath", sync = true)
    public String read(String root) throws IOException {
        if (!existsPath(root)) {
            return "";
        }
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            try (FSDataInputStream stream = fileSystem.open(new Path(root))) {
                return IoUtil.readUtf8(stream);
            }
        }
    }

    public void write(String root, String text, Boolean overwrite) throws IOException {
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            try (FSDataOutputStream stream = fileSystem.create(new Path(root), overwrite)) {
                stream.writeBytes(text);
            }
        }
    }

    @SuppressWarnings("SpringCacheableMethodCallsInspection")
    public void download(String root, OutputStream outputStream) throws IOException {
        if (!existsPath(root)) {
            return;
        }
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            try (FSDataInputStream stream = fileSystem.open(new Path(root))) {
                IoUtil.copy(stream, outputStream);
            }
        }
    }

    @Cacheable(value = "size-hpath", sync = true)
    public Long size(String root) throws IOException {
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            return fileSystem.getContentSummary(new Path(root)).getLength();
        }
    }

    @Cacheable(value = "count-hpath", sync = true)
    public Long count(String root) throws IOException {
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            ContentSummary summary = fileSystem.getContentSummary(new Path(root));
            return summary.getFileCount() + summary.getDirectoryCount();
        }
    }

    @Cacheable(value = "file-count-hpath", sync = true)
    public Long countFiles(String root) throws IOException {
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            return fileSystem.getContentSummary(new Path(root)).getFileCount();
        }
    }

    @Cacheable(value = "directory-count-hpath", sync = true)
    public Long countDirectories(String root) throws IOException {
        try (FileSystem fileSystem = FileSystem.get(new Configuration())) {
            return fileSystem.getContentSummary(new Path(root)).getDirectoryCount();
        }
    }
}
