package com.lanyuanxiaoyao.service.info.service;

import club.kingon.sql.builder.SelectSqlBuilder;
import club.kingon.sql.builder.SqlBuilder;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.service.configuration.entity.PageResponse;
import com.lanyuanxiaoyao.service.configuration.entity.info.JobAndMetas;
import com.lanyuanxiaoyao.service.configuration.entity.info.JobIdAndAlias;
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;

import static com.lanyuanxiaoyao.service.common.SQLConstants.HudiCollectBuild.*;

/**
 * @author lanyuanxiaoyao
 * @date 2023-04-24
 */
@CacheConfig(cacheManager = "normal-cache")
@Service
public class InfoService extends BaseService {
    private static final Logger logger = LoggerFactory.getLogger(InfoService.class);
    private final JdbcTemplate mysqlJdbcTemplate;
    private final TransactionTemplate mysqlTransactionTemplate;
    private final FlinkJobService flinkJobService;
    private final TableMetaService tableMetaService;

    public InfoService(JdbcTemplate mysqlJdbcTemplate, TransactionTemplate mysqlTransactionTemplate, FlinkJobService flinkJobService, TableMetaService tableMetaService) {
        this.mysqlJdbcTemplate = mysqlJdbcTemplate;
        this.mysqlTransactionTemplate = mysqlTransactionTemplate;
        this.flinkJobService = flinkJobService;
        this.tableMetaService = tableMetaService;
    }

    private SqlBuilder generateJobIdAndAliasCriteria(
            SelectSqlBuilder builder,
            Integer page,
            Integer count,
            Long flinkJobId,
            String alias,
            String order,
            String direction,
            ImmutableList<String> selectHudiTableType,
            ImmutableList<String> selectedRunMode,
            ImmutableList<String> selectedCompactionStatus,
            boolean limited
    ) {
        int limit = Math.max(count, 1);
        int offset = limit * Math.max(page - 1, 0);
        return TableMetaService.generateSimpleTableMetaList(builder)
                .andLike(ObjectUtil.isNotNull(flinkJobId), TbAppFlinkJobConfig.ID_A, flinkJobId)
                .andLike(ObjectUtil.isNotNull(alias), TbAppCollectTableInfo.ALIAS_A, alias)
                .andIn(ObjectUtil.isNotEmpty(selectHudiTableType), TbAppCollectTableInfo.TGT_TABLE_A, selectHudiTableType)
                .andIn(ObjectUtil.isNotEmpty(selectedRunMode), TbAppFlinkJobConfig.RUN_MODE_A, selectedRunMode)
                .andIn(ObjectUtil.isNotEmpty(selectedCompactionStatus), TbAppHudiSyncState.COMPACTION_STATUS_A, selectedCompactionStatus)
                .orderBy(StrUtil.isNotBlank(order) && StrUtil.isNotBlank(direction), () -> StrUtil.format("{} {}", order, direction))
                .limit(limited, offset, limit);
    }

    @Cacheable(value = "job-id-alias", sync = true)
    @Retryable(Throwable.class)
    public PageResponse<JobIdAndAlias> findAllJobIdAndAlias(
            Integer page,
            Integer count,
            Long flinkJobId,
            String alias,
            String order,
            String direction,
            ImmutableList<String> selectHudiTableType,
            ImmutableList<String> selectedRunMode,
            ImmutableList<String> selectedCompactionStatus
    ) {
        return mysqlTransactionTemplate.execute(status -> {
            String orderField = null;
            String orderDirection = null;
            if (order != null && direction != null) {
                switch (order) {
                    case "source_start_time":
                    case "sourceStartTime":
                        orderField = "tahss.source_start_time";
                        break;
                    case "source_checkpoint_time":
                    case "sourceCheckpointTime":
                        orderField = "tahss.source_checkpoint_time";
                        break;
                    case "source_publish_time":
                    case "sourcePublishTime":
                        orderField = "tahss.source_publish_time";
                        break;
                    case "source_operation_time":
                    case "sourceOperationTime":
                        orderField = "tahss.source_op_time";
                        break;
                    case "compaction_start_time":
                    case "compactionStartTime":
                        orderField = "tahss.compaction_start_time";
                        break;
                    case "compaction_finish_time":
                    case "compactionFinishTime":
                        orderField = "tahss.compaction_finish_time";
                        break;
                }
                switch (direction) {
                    case "desc":
                    case "DESC":
                    case "down":
                    case "DOWN":
                        orderDirection = "desc";
                        break;
                    case "asc":
                    case "ASC":
                    case "up":
                    case "UP":
                        orderDirection = "asc";
                        break;
                }
            }
            Long total = mysqlJdbcTemplate.queryForObject(
                    generateJobIdAndAliasCriteria(
                            SqlBuilder.select(COUNT),
                            page,
                            count,
                            flinkJobId,
                            alias,
                            orderField,
                            orderDirection,
                            selectHudiTableType,
                            selectedRunMode,
                            selectedCompactionStatus,
                            false
                    ).build(),
                    Long.class
            );
            String listSQL = generateJobIdAndAliasCriteria(
                    SqlBuilder.select(TbAppFlinkJobConfig.ID_A, TbAppCollectTableInfo.ALIAS_A),
                    page,
                    count,
                    flinkJobId,
                    alias,
                    orderField,
                    orderDirection,
                    selectHudiTableType,
                    selectedRunMode,
                    selectedCompactionStatus,
                    true
            ).build();
            List<JobIdAndAlias> list = mysqlJdbcTemplate.query(listSQL, (rs, row) -> new JobIdAndAlias(rs.getLong(1), rs.getString(2)));
            return new PageResponse<>(list, total);
        });
    }

    @Cacheable(value = "job-metas", sync = true)
    @Retryable(Throwable.class)
    public ImmutableList<JobAndMetas> jobAndMetas() {
        return flinkJobService.flinkJobs()
                .collect(job -> new JobAndMetas(job, tableMetaService.tableMetaList(job.getId())));
    }
}
