package com.lanyuanxiaoyao.service.info.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.eshore.odcp.hudi.connector.entity.FlinkJob;
import com.eshore.odcp.hudi.connector.entity.SyncState;
import com.eshore.odcp.hudi.connector.entity.TableMeta;
import com.eshore.odcp.hudi.connector.utils.database.DatabaseService;
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.eshore.odcp.hudi.connector.Constants.DATABASE_NAME;

/**
 * @author lanyuanxiaoyao
 * @date 2023-04-24
 */
@CacheConfig(cacheManager = "normal-cache")
@Service
public class InfoService {
    private static final Logger logger = LoggerFactory.getLogger(InfoService.class);

    private final DatabaseService databaseService;
    private final JdbcTemplate mysqlJdbcTemplate;
    private final TransactionTemplate mysqlTransactionTemplate;

    public InfoService(DatabaseService databaseService, JdbcTemplate mysqlJdbcTemplate, TransactionTemplate mysqlTransactionTemplate) {
        this.databaseService = databaseService;
        this.mysqlJdbcTemplate = mysqlJdbcTemplate;
        this.mysqlTransactionTemplate = mysqlTransactionTemplate;
    }

    @Cacheable(value = "sync-state", sync = true, key = "#flinkJobId.toString()+#alias")
    @Retryable(Throwable.class)
    public SyncState syncState(Long flinkJobId, String alias) {
        return databaseService.getSyncState(flinkJobId, alias);
    }

    @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> selectedRunMode,
            ImmutableList<String> selectedCompactionStatus
    ) {
        return mysqlTransactionTemplate.execute(status -> {
            int limit = Math.max(count, 1);
            int offset = limit * Math.max(page - 1, 0);
            String searchFlinkJobId = flinkJobId == null ? "" : "and job.id like '%" + flinkJobId + "%'\n";
            String searchAlias = alias == null ? "" : "and info.alias like '%" + alias + "%'\n";
            String filterRunMode = selectedRunMode == null || selectedRunMode.isEmpty()
                    ? ""
                    : "and job.run_mode in ('" + selectedRunMode.makeString("','") + "')\n";
            String filterCompactionStatus = selectedCompactionStatus == null || selectedCompactionStatus.isEmpty()
                    ? ""
                    : "and state.compaction_status in ('" + selectedCompactionStatus.makeString("','") + "')\n";
            String orderBy = "";
            if (order != null && direction != null) {
                String orderField = null;
                switch (order) {
                    case "source_start_time":
                    case "sourceStartTime":
                        orderField = "state.source_start_time";
                        break;
                    case "source_checkpoint_time":
                    case "sourceCheckpointTime":
                        orderField = "state.source_checkpoint_time";
                        break;
                    case "source_publish_time":
                    case "sourcePublishTime":
                        orderField = "state.source_publish_time";
                        break;
                    case "source_operation_time":
                    case "sourceOperationTime":
                        orderField = "state.source_op_time";
                        break;
                    case "compaction_start_time":
                    case "compactionStartTime":
                        orderField = "state.compaction_start_time";
                        break;
                    case "compaction_finish_time":
                    case "compactionFinishTime":
                        orderField = "state.compaction_finish_time";
                        break;
                }
                String orderDirection = null;
                switch (direction) {
                    case "desc":
                    case "DESC":
                    case "down":
                    case "DOWN":
                        orderDirection = "desc";
                        break;
                    case "asc":
                    case "ASC":
                    case "up":
                    case "UP":
                        orderDirection = "asc";
                        break;
                }
                if (ObjectUtil.isNotNull(orderField) && ObjectUtil.isNotNull(direction)) {
                    orderBy = StrUtil.format("order by {} {}\n", orderField, orderDirection);
                }
            }
            Long total = mysqlJdbcTemplate.queryForObject(
                    "select count(*)\n" +
                            "from " + DATABASE_NAME + ".tb_app_flink_job_config job,\n" +
                            "     " + DATABASE_NAME + ".tb_app_collect_table_info info,\n" +
                            "     " + DATABASE_NAME + ".tb_app_hudi_sync_state state\n" +
                            "where job.id = info.flink_job_id\n" +
                            "and state.id = concat(job.id, '-', info.alias)\n" +
                            searchFlinkJobId +
                            searchAlias +
                            filterRunMode +
                            filterCompactionStatus +
                            orderBy,
                    Long.class
            );
            List<JobIdAndAlias> list = mysqlJdbcTemplate.query(
                    "select job.id, info.alias\n" +
                            "from " + DATABASE_NAME + ".tb_app_flink_job_config job,\n" +
                            "     " + DATABASE_NAME + ".tb_app_collect_table_info info,\n" +
                            "     " + DATABASE_NAME + ".tb_app_hudi_sync_state state\n" +
                            "where job.id = info.flink_job_id\n" +
                            "and state.id = concat(job.id, '-', info.alias)\n" +
                            searchFlinkJobId +
                            searchAlias +
                            filterRunMode +
                            filterCompactionStatus +
                            orderBy +
                            "limit " + limit + " offset " + offset,
                    (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 databaseService.findAllFlinkJob().collect(job -> new JobAndMetas(job, databaseService.findTableMeta(job.getId()).toList()));
    }

    @Cacheable(value = "flink-jobs", sync = true)
    @Retryable(Throwable.class)
    public ImmutableList<FlinkJob> flinkJobs() {
        return databaseService.findAllFlinkJob();
    }

    @Cacheable(value = "flink-jobs", sync = true, key = "#flinkJobId")
    @Retryable(Throwable.class)
    public FlinkJob flinkJob(Long flinkJobId) {
        return databaseService.getFlinkJob(flinkJobId);
    }

    @Cacheable(value = "table-metas", sync = true)
    @Retryable(Throwable.class)
    public ImmutableList<TableMeta> tableMetas() {
        return flinkJobs().flatCollect(job -> databaseService.findTableMeta(job.getId()));
    }

    @Cacheable(value = "table-metas", sync = true, key = "#flinkJobId")
    @Retryable(Throwable.class)
    public ImmutableList<TableMeta> tableMetas(Long flinkJobId) {
        return databaseService.findTableMeta(flinkJobId);
    }

    @Cacheable(value = "table-metas", sync = true, key = "#flinkJobId.toString()+#alias")
    @Retryable(Throwable.class)
    public TableMeta tableMeta(Long flinkJobId, String alias) {
        return databaseService.getTableMeta(flinkJobId, alias);
    }
}
