package com.lanyuanxiaoyao.service.command.commands;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.service.command.commands.jobs.*;
import com.lanyuanxiaoyao.service.command.provider.SearchTypeValueProvider;
import com.lanyuanxiaoyao.service.command.utils.SearchUtils;
import com.lanyuanxiaoyao.service.common.entity.FlinkJob;
import com.lanyuanxiaoyao.service.common.entity.TableMeta;
import com.lanyuanxiaoyao.service.configuration.entity.info.JobAndMetas;
import com.lanyuanxiaoyao.service.forest.service.InfoService;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.eclipse.collections.api.list.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.shell.CompletionContext;
import org.springframework.shell.CompletionProposal;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import org.springframework.shell.standard.ValueProviderSupport;
import org.springframework.shell.table.BorderStyle;
import org.springframework.shell.table.TableBuilder;
import org.springframework.shell.table.TableModelBuilder;

/**
 * 任务操作
 *
 * @author ZhangJiacheng
 * @date 2022-03-24
 */
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@ShellComponent("Config job")
public class JobCommand {
    private static final Logger logger = LoggerFactory.getLogger(JobCommand.class);

    private final InfoService infoService;

    public JobCommand(InfoService infoService) {
        this.infoService = infoService;
    }

    @ShellMethod("显示 Flink job 配置信息")
    public String jobAll(
            @ShellOption(help = "搜索模式",
                    defaultValue = "CONTAINS",
                    valueProvider = SearchTypeValueProvider.class) SearchUtils.Type type,
            @ShellOption(help = "搜索值 (除了 REGEX 模式, 其余可使用英文逗号分隔多个值)",
                    defaultValue = "") String pattern
    ) {
        ImmutableList<JobAndMetas> jobAndMetas = infoService.allFlinkJobId()
                .collect(id -> {
                    FlinkJob flinkJob = infoService.flinkJobDetail(id);
                    ImmutableList<TableMeta> metas = infoService.tableMetaList(id)
                            .select(meta -> SearchUtils.search(type, pattern, meta.getSchema() + meta.getAlias()));
                    return new JobAndMetas(flinkJob, metas);
                })
                .reject(job -> job.getMetas().isEmpty());
        TableModelBuilder<Object> builder = new TableModelBuilder<>();
        builder.addRow();
        builder.addValue("#");
        builder.addValue("Id");
        builder.addValue("Name/Table");
        builder.addValue("Bucket");
        builder.addValue("Compaction seconds");
        builder.addValue("Hdfs");
        for (int index = 0; index < jobAndMetas.size(); index++) {
            JobAndMetas job = jobAndMetas.get(index);
            builder.addRow();
            builder.addValue(StrUtil.format("{}-0", index + 1));
            builder.addValue(job.getJob().getId());
            builder.addValue(job.getJob().getName());
            builder.addValue("");
            builder.addValue("");
            builder.addValue("");
            if (ObjectUtil.isNotNull(job.getMetas()) && ObjectUtil.isNotEmpty(job.getMetas())) {
                ImmutableList<TableMeta> tableMetas = job.getMetas();
                for (int indexSub = 0; indexSub < tableMetas.size(); indexSub++) {
                    TableMeta meta = tableMetas.get(indexSub);
                    builder.addRow();
                    builder.addValue(StrUtil.format("{}-{}", index + 1, indexSub + 1));
                    builder.addValue("");
                    builder.addValue(meta.getTable());
                    builder.addValue(meta.getHudi().getBucketIndexNumber());
                    builder.addValue(meta.getHudi().getCompactionDeltaSeconds());
                    builder.addValue(meta.getHudi().getTargetHdfsPath());
                }
            }
        }
        return new TableBuilder(builder.build())
                .addHeaderAndVerticalsBorders(BorderStyle.oldschool)
                .build()
                .render(200);
    }

    @ShellMethod("批量新增 Flink job 配置")
    public void jobAddBatch(@ShellOption(help = "配置文件路径") String source) throws IOException {
        Path sourcePath = Paths.get(source);
        if (Files.notExists(sourcePath)) {
            throw new RuntimeException(StrUtil.format("{} not found", source));
        }
        Files.lines(sourcePath)
                .filter(StrUtil::isNotBlank)
                .map(line -> line.split("\\s+"))
                .forEach(fields -> {
                    TableType type = TableType.valueOf(fields[0]);
                    switch (type) {
                        case SMALL:
                            jobAdd(TableType.SMALL, fields[1], TableMeta.SourceType.valueOf(fields[2]), fields[3], fields[4], fields[5], fields[6]);
                            break;
                        case BIG:
                            jobAdd(TableType.BIG, fields[1], TableMeta.SourceType.valueOf(fields[2]), fields[3], fields[4], fields[5], fields[6]);
                            break;
                        case ACCT_SMALL:
                            jobAdd(TableType.ACCT_SMALL, fields[1], TableMeta.SourceType.valueOf(fields[2]), fields[3], fields[4], fields[5], fields[6]);
                            break;
                        case ACCT_BIG:
                            jobAdd(TableType.ACCT_BIG, fields[1], TableMeta.SourceType.valueOf(fields[2]), fields[3], fields[4], fields[5], fields[6]);
                            break;
                        case ACCT_ITEM:
                            jobAdd(TableType.ACCT_ITEM, fields[1], TableMeta.SourceType.valueOf(fields[2]), fields[3], fields[4], fields[5], fields[6]);
                            break;
                        default:
                            throw new RuntimeException("不支持的表类型: " + type);
                    }
                });
    }

    /**
     * udal__crm_order crm_order order_balance_his SMALL hdfs://b1/apps/iap/hive pulsar://132.122.113.171:16660,132.122.113.172:16660,132.122.113.173:16660,132.122.113.174:16660,132.122.113.175:16660,132.122.113.176:16660 persistent://odcp/crm_order/ord_prod_inst_acc_num dws_crm_order dws_order_balance_his
     */
    @ShellMethod("新增 Flink job 配置")
    public void jobAdd(
            @ShellOption(help = "表类型 (BIG 为大表, SMALL 为小表, ACCT 为 ACCT 表)",
                    valueProvider = TableTypeValueProvider.class) TableType type,
            @ShellOption(help = "数据源") String database,
            @ShellOption(help = "数据源类型",
                    valueProvider = DatabaseTypeValueProvider.class) TableMeta.SourceType databaseType,
            @ShellOption(help = "Scheme") String schema,
            @ShellOption(help = "表名") String table,
            @ShellOption(help = "HDFS 路径前缀") String hdfs,
            @ShellOption(help = "Pulsar Address") String pulsarAddress
    ) {
        if (infoService.existsTableByHdfs(StrUtil.format("{}/dws_{}/dws_{}", hdfs, schema, table))) {
            throw new RuntimeException(StrUtil.format("tb_app_collect_table_info 已存在指定的 HDFS 路径 {}", StrUtil.format("{}/dws_{}/dws_{}", hdfs, schema, table)));
        }
        switch (type) {
            case SMALL:
                new SmallTableAdder(infoService, database, databaseType.name(), schema, table, pulsarAddress, hdfs).execute();
                break;
            case BIG:
                new BigTableAdder(infoService, database, databaseType.name(), schema, table, pulsarAddress, hdfs).execute();
                break;
            case ACCT_SMALL:
                new AcctSmallTableAdder(infoService, database, databaseType.name(), schema, table, pulsarAddress, hdfs).execute();
                break;
            case ACCT_BIG:
                new AcctBigTableAdder(infoService, database, databaseType.name(), schema, table, pulsarAddress, hdfs).execute();
                break;
            case ACCT_ITEM:
                new AcctItemTableAdder(infoService, database, databaseType.name(), schema, table, pulsarAddress, hdfs).execute();
                break;
            default:
                throw new RuntimeException("不支持的表类型: " + type);
        }
    }

    public enum TableType {
        /**
         * 小表
         */
        SMALL,
        /**
         * 大表
         */
        BIG,
        /**
         * ACCT 小表
         */
        ACCT_SMALL,
        /**
         * ACCT 大表
         */
        ACCT_BIG,
        /**
         * ACCT_ITEM
         */
        ACCT_ITEM,
    }

    public static final class TableTypeValueProvider extends ValueProviderSupport {
        @Override
        public List<CompletionProposal> complete(MethodParameter parameter, CompletionContext context, String[] hints) {
            return ListUtil.of(
                    new CompletionProposal("SMALL"),
                    new CompletionProposal("BIG"),
                    new CompletionProposal("ACCT"),
                    new CompletionProposal("ACCT_ITEM")
            );
        }
    }

    public static final class DatabaseTypeValueProvider extends ValueProviderSupport {
        @Override
        public List<CompletionProposal> complete(MethodParameter parameter, CompletionContext completionContext, String[] hints) {
            return ListUtil.of(
                    new CompletionProposal("UDAL"),
                    new CompletionProposal("TELEPG")
            );
        }
    }
}
