package com.lanyuanxiaoyao.service.info.service;

import club.kingon.sql.builder.SelectSqlBuilder;
import club.kingon.sql.builder.SqlBuilder;
import club.kingon.sql.builder.entry.Alias;
import club.kingon.sql.builder.entry.Column;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.service.common.SQLConstants;
import com.lanyuanxiaoyao.service.configuration.entity.PageResponse;
import com.lanyuanxiaoyao.service.configuration.entity.info.CompactionMetrics;
import java.sql.Timestamp;
import java.util.List;
import org.eclipse.collections.api.list.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * 压缩
 *
 * @author lanyuanxiaoyao
 * @date 2024-01-03
 */
@CacheConfig(cacheManager = "normal-cache")
@Service
public class CompactionService extends BaseService {
    private static final Logger logger = LoggerFactory.getLogger(CompactionService.class);
    private final JdbcTemplate mysqlJdbcTemplate;
    private final TransactionTemplate mysqlTransactionTemplate;

    public CompactionService(JdbcTemplate mysqlJdbcTemplate, TransactionTemplate mysqlTransactionTemplate) {
        this.mysqlJdbcTemplate = mysqlJdbcTemplate;
        this.mysqlTransactionTemplate = mysqlTransactionTemplate;
    }

    private SqlBuilder generateCompactionMetricsCriteria(
            SelectSqlBuilder builder,
            Integer page,
            Integer count,
            Long flinkJobId,
            String alias,
            String order,
            String direction,
            ImmutableList<Boolean> filterCompletes,
            boolean limited
    ) {
        int limit = Math.max(count, 1);
        int offset = limit * Math.max(page - 1, 0);
        Alias m1 = Alias.of(
                SqlBuilder.selectAll()
                        .from(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics._alias_)
                        .whereEq(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics.TYPE_A, "pre")
                        .andEq(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics.FLINK_JOB_ID_A, flinkJobId)
                        .andEq(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics.ALIAS_A, alias),
                "m1"
        );
        Alias m2 = Alias.of(
                SqlBuilder.selectAll()
                        .from(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics._alias_)
                        .whereEq(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics.TYPE_A, "complete")
                        .andEq(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics.FLINK_JOB_ID_A, flinkJobId)
                        .andEq(SQLConstants.HudiCollectBuild.TbAppHudiCompactionMetrics.ALIAS_A, alias),
                "m2"
        );
        return builder
                .from(m1)
                .leftJoin(m2)
                .onEq(column(m1, "flink_job_id"), Column.as(column(m2, "flink_job_id")))
                .andEq(column(m1, "alias"), Column.as(column(m2, "alias")))
                .andEq(column(m1, "application_id"), Column.as(column(m2, "application_id")))
                .andEq(column(m1, "compaction_plan_instant"), Column.as(column(m2, "compaction_plan_instant")))
                .whereNotNull(filterCompletes.anySatisfy(b -> b), column(m2, "type"))
                .orNull(filterCompletes.anySatisfy(b -> !b), column(m2, "type"))
                .orderBy(StrUtil.isNotBlank(order) && StrUtil.isNotBlank(direction), StrUtil.format("m1.{} {}", order, direction))
                .limit(limited, offset, count);
    }

    @Cacheable(value = "compaction-metrics", sync = true)
    @Retryable(Throwable.class)
    public PageResponse<CompactionMetrics> findAllCompactionMetrics(
            Integer page,
            Integer count,
            Long flinkJobId,
            String alias,
            String order,
            String direction,
            ImmutableList<Boolean> filterCompletes
    ) {
        return mysqlTransactionTemplate.execute(status -> {
            Long total = mysqlJdbcTemplate.queryForObject(
                    generateCompactionMetricsCriteria(
                            SqlBuilder.select(COUNT),
                            page,
                            count,
                            flinkJobId,
                            alias,
                            order,
                            direction,
                            filterCompletes,
                            false
                    ).build(),
                    Long.class
            );
            List<CompactionMetrics> list = mysqlJdbcTemplate.query(
                    generateCompactionMetricsCriteria(
                            SqlBuilder.select(
                                    "m1.flink_job_id",
                                    "m1.alias",
                                    "m1.application_id",
                                    "m1.cluster",
                                    "m1.compaction_plan_instant",
                                    "m2.type is not null as is_complete",
                                    "m1.update_time as started_time",
                                    "m2.update_time as finished_time",
                                    "m1.total_scan_time as pre_total_scan_time",
                                    "m1.total_log_files_compacted as pre_total_log_files_compacted",
                                    "m1.total_log_files_size as pre_total_log_files_size",
                                    "m1.total_records_deleted as pre_total_records_deleted",
                                    "m1.total_records_updated as pre_total_records_updated",
                                    "m1.total_records_compacted as pre_total_records_compacted",
                                    "m2.total_scan_time as complete_total_scan_time",
                                    "m2.total_log_files_compacted as complete_total_log_files_compacted",
                                    "m2.total_log_files_size as complete_total_log_files_size",
                                    "m2.total_records_deleted as complete_total_records_deleted",
                                    "m2.total_records_updated as complete_total_records_updated",
                                    "m2.total_records_compacted as complete_total_records_compacted"
                            ),
                            page,
                            count,
                            flinkJobId,
                            alias,
                            order,
                            direction,
                            filterCompletes,
                            true
                    ).build(),
                    (rs, row) -> {
                        boolean isComplete = rs.getBoolean(6);
                        if (ObjectUtil.isNull(isComplete)) {
                            isComplete = false;
                        }
                        Timestamp startedTimestamp = rs.getTimestamp(7);
                        long startedTime = 0;
                        if (ObjectUtil.isNotNull(startedTimestamp)) {
                            startedTime = startedTimestamp.getTime();
                        }
                        Timestamp finishedTimestamp = rs.getTimestamp(8);
                        long finishedTime = 0;
                        if (ObjectUtil.isNotNull(finishedTimestamp)) {
                            finishedTime = finishedTimestamp.getTime();
                        }
                        CompactionMetrics.Statistics before = new CompactionMetrics.Statistics(
                                rs.getLong(9),
                                rs.getLong(10),
                                rs.getLong(11),
                                rs.getLong(12),
                                rs.getLong(13),
                                rs.getLong(14)
                        );
                        CompactionMetrics.Statistics after = new CompactionMetrics.Statistics(
                                rs.getLong(15),
                                rs.getLong(16),
                                rs.getLong(17),
                                rs.getLong(18),
                                rs.getLong(19),
                                rs.getLong(20)
                        );
                        return new CompactionMetrics(
                                rs.getLong(1),
                                rs.getString(2),
                                rs.getString(3),
                                rs.getString(4),
                                rs.getString(5),
                                isComplete,
                                startedTime,
                                finishedTime,
                                before,
                                after
                        );
                    }
            );
            return new PageResponse<>(list, total);
        });
    }
}
