package com.lanyuanxiaoyao.service.sync.utils;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.lanyuanxiaoyao.service.common.Constants;
import com.lanyuanxiaoyao.service.common.entity.FlinkJob;
import com.lanyuanxiaoyao.service.common.entity.TableMeta;
import com.lanyuanxiaoyao.service.sync.configuration.GlobalConfiguration;
import com.lanyuanxiaoyao.service.sync.configuration.RetryPolicyProvider;
import dev.failsafe.Failsafe;
import java.time.Instant;
import java.util.Map;
import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hudi.common.model.HoodieCommitMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 状态输出
 *
 * @author ZhangJiacheng
 * @date 2022-07-05
 */
public class StatusUtils {
    private static final Logger logger = LoggerFactory.getLogger(StatusUtils.class);

    private static final ObjectMapper MAPPER = JacksonUtils.getMapper();
    private static final int HTTP_TIMEOUT = (int) Constants.MINUTE;

    public static void syncStart(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta) {
        logger.info("Enter method: syncStart[configuration, flinkJob, tableMeta]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta);
        try {
            Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                    .run(() ->
                            HttpUtil.createGet(
                                            StrUtil.format(
                                                    "{}/api/sync_start?flink_job_id={}&alias={}&database={}&schema={}&table={}&cluster={}&application_id={}",
                                                    LoadBalance.getCustomPublishUrl(configuration),
                                                    flinkJob.getId(),
                                                    tableMeta.getAlias(),
                                                    tableMeta.getSource(),
                                                    tableMeta.getSchema(),
                                                    tableMeta.getTable(),
                                                    configuration.getCluster(),
                                                    configuration.getApplicationId()
                                            )
                                    )
                                    .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                    .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                    .timeout(HTTP_TIMEOUT)
                                    .execute()
                    );
        } catch (Exception e) {
            logger.warn("sync start metrics submit failure");
        }
    }

    public static void syncCheckpoint(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta, String messageId, Long publishTime) {
        logger.info("Enter method: syncCheckpoint[configuration, flinkJob, tableMeta, messageId, publishTime]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta + "," + "messageId:" + messageId + "," + "publishTime:" + publishTime);
        try {
            Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                    .run(() -> HttpUtil.createGet(
                                            StrUtil.format(
                                                    "{}/api/sync_checkpoint_state?flink_job_id={}&alias={}&message_id={}&publish_time={}",
                                                    LoadBalance.getCustomPublishUrl(configuration),
                                                    flinkJob.getId(),
                                                    tableMeta.getAlias(),
                                                    messageId,
                                                    publishTime
                                            )
                                    )
                                    .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                    .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                    .timeout(HTTP_TIMEOUT)
                                    .execute()
                    );
        } catch (Exception e) {
            logger.warn("sync checkpoint metrics submit failure");
        }
    }

    public static void syncOperation(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta) {
        syncOperation(configuration, flinkJob, tableMeta, null);
    }

    public static void syncOperation(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta, Long operationTime) {
        logger.info("Enter method: syncOperation[configuration, flinkJob, tableMeta, operationTime]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta + "," + "operationTime:" + operationTime);
        try {
            Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                    .run(() -> {
                        if (ObjectUtil.isNull(operationTime)) {
                            HttpUtil.createGet(
                                            StrUtil.format(
                                                    "{}/api/sync_operation_state?flink_job_id={}&alias={}",
                                                    LoadBalance.getCustomPublishUrl(configuration),
                                                    flinkJob.getId(),
                                                    tableMeta.getAlias()
                                            )
                                    )
                                    .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                    .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                    .timeout(HTTP_TIMEOUT)
                                    .execute();
                        } else {
                            HttpUtil.createGet(
                                            StrUtil.format(
                                                    "{}/api/sync_operation_state?flink_job_id={}&alias={}&operation_time={}",
                                                    LoadBalance.getCustomPublishUrl(configuration),
                                                    flinkJob.getId(),
                                                    tableMeta.getAlias(),
                                                    operationTime
                                            )
                                    )
                                    .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                    .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                    .timeout(HTTP_TIMEOUT)
                                    .execute();
                        }
                    });
        } catch (Exception e) {
            logger.warn("sync operation metrics submit failure");
        }
    }

    public static void compactionStart(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta) {
        logger.info("Enter method: compactionStart[configuration, flinkJob, tableMeta]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta);
        try {
            Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                    .run(() ->
                            HttpUtil.createGet(
                                            StrUtil.format(
                                                    "{}/api/compaction_start?flink_job_id={}&alias={}&type={}&cluster={}&application_id={}",
                                                    LoadBalance.getCustomPublishUrl(configuration),
                                                    flinkJob.getId(),
                                                    tableMeta.getAlias(),
                                                    EnumUtil.toString(tableMeta.getSourceType()),
                                                    configuration.getCluster(),
                                                    configuration.getApplicationId()
                                            )
                                    )
                                    .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                    .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                    .timeout(HTTP_TIMEOUT)
                                    .execute()
                    );
        } catch (Exception e) {
            logger.warn("compaction start metrics submit failure");
        }
    }

    public static void compactionPreCommit(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta, String instant, Map<String, Long> metadata) {
        logger.info("Enter method: compactionPreCommit[configuration, flinkJob, tableMeta, instant, metadata]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta + "," + "instant:" + instant + "," + "metadata:" + metadata);
        try {
            Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                    .run(() -> HttpUtil.createPost(
                                            StrUtil.format(
                                                    "{}/api/compaction_pre_commit?flink_job_id={}&alias={}&instant={}&cluster={}&application_id={}",
                                                    LoadBalance.getCustomPublishUrl(configuration),
                                                    flinkJob.getId(),
                                                    tableMeta.getAlias(),
                                                    instant,
                                                    configuration.getCluster(),
                                                    configuration.getApplicationId()
                                            )
                                    )
                                    .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                    .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                    .body(MAPPER.writeValueAsString(metadata))
                                    .timeout(HTTP_TIMEOUT)
                                    .execute()
                    );
        } catch (Exception e) {
            logger.warn("compaction pre commit metrics submit failure");
        }
    }

    public static void compactionCommit(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta, String instant, HoodieCommitMetadata metadata) {
        logger.info("Enter method: compactionCommit[configuration, flinkJob, tableMeta, instant, metadata]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta + "," + "instant:" + instant + "," + "metadata:" + metadata);
        try {
            Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                    .run(() -> HttpUtil.createPost(
                                            StrUtil.format(
                                                    "{}/api/compaction_commit?flink_job_id={}&alias={}&instant={}&cluster={}&application_id={}",
                                                    LoadBalance.getCustomPublishUrl(configuration),
                                                    flinkJob.getId(),
                                                    tableMeta.getAlias(),
                                                    instant,
                                                    configuration.getCluster(),
                                                    configuration.getApplicationId()
                                            )
                                    )
                                    .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                    .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                    .body(MAPPER.writeValueAsString(metadata))
                                    .timeout(HTTP_TIMEOUT)
                                    .execute()
                    );
        } catch (Exception e) {
            logger.warn("compaction commit metrics submit failure");
        }
    }

    public static void compactionFinish(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta, String message, Exception exception) {
        logger.info("Enter method: compactionFinish[configuration, flinkJob, tableMeta, message, exception]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta + "," + "message:" + message + "," + "exception:" + exception);
        try {
            Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                    .run(() -> {
                        boolean success = (exception == null);
                        HttpUtil.createPost(StrUtil.format(
                                        "{}/api/compaction_finish?flink_job_id={}&alias={}&time={}&state={}",
                                        LoadBalance.getCustomPublishUrl(configuration),
                                        flinkJob.getId(),
                                        tableMeta.getAlias(),
                                        Instant.now().toEpochMilli(),
                                        success
                                ))
                                .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                .body(success ? message == null ? "" : message : exception.toString(), "text/plain")
                                .timeout(HTTP_TIMEOUT)
                                .execute()
                                .close();
                    });
        } catch (Exception e) {
            logger.warn("compaction finish metrics submit failure");
        }
    }

    public static void versionUpdate(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta, String version, String opts) {
        logger.info("Enter method: versionUpdate[configuration, flinkJob, tableMeta, version, opts]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta + "," + "version:" + version + "," + "opts:" + opts);
        Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                .run(() ->
                        HttpUtil.createGet(
                                        StrUtil.format(
                                                "{}/api/version_update?flink_job_id={}&alias={}&version={}&opts={}",
                                                LoadBalance.getCustomPublishUrl(configuration),
                                                flinkJob.getId(),
                                                tableMeta.getAlias(),
                                                version,
                                                opts
                                        )
                                )
                                .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                .timeout(HTTP_TIMEOUT)
                                .execute()
                );
    }

    public static void compactionLatestOpTs(GlobalConfiguration configuration, FlinkJob flinkJob, TableMeta tableMeta, Long latestOpTs) {
        logger.info("Enter method: compactionLatestOpTs[configuration, flinkJob, tableMeta, latestOpTs]. " + "configuration:" + configuration + "," + "flinkJob:" + flinkJob + "," + "tableMeta:" + tableMeta + "," + "latestOpTs:" + latestOpTs);
        Failsafe.with(RetryPolicyProvider.HTTP_RETRY)
                .run(() ->
                        HttpUtil.createGet(
                                        StrUtil.format(
                                                "{}/api/compaction_latest_operation_time?flink_job_id={}&alias={}&latest_op_ts={}",
                                                LoadBalance.getCustomPublishUrl(configuration),
                                                flinkJob.getId(),
                                                tableMeta.getAlias(),
                                                latestOpTs
                                        )
                                )
                                .header(Constants.API_HEADER_NAME, Constants.API_VERSION)
                                .basicAuth(Constants.SPRING_SECURITY_USERNAME, Constants.SPRING_SECURITY_PASSWORD_PLAIN)
                                .timeout(HTTP_TIMEOUT)
                                .execute()
                );
    }
}
