package com.lanyuanxiaoyao.service.info.service;

import club.kingon.sql.builder.SelectSqlBuilder;
import club.kingon.sql.builder.SqlBuilder;
import club.kingon.sql.builder.WhereSqlBuilder;
import club.kingon.sql.builder.entry.Column;
import cn.hutool.core.lang.Pair;
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.JobIdAndAlias;
import com.lanyuanxiaoyao.service.configuration.entity.info.VersionUpdated;
import com.lanyuanxiaoyao.service.info.configuration.SQLLoggerProvider;
import java.util.List;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.map.ImmutableMap;
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.*;

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

    public VersionService(JdbcTemplate mysqlJdbcTemplate, TransactionTemplate mysqlTransactionTemplate, SQLLoggerProvider.SQLLogger sqlLogger) {
        this.mysqlJdbcTemplate = mysqlJdbcTemplate;
        this.mysqlTransactionTemplate = mysqlTransactionTemplate;
        this.sqlLogger = sqlLogger;
    }

    private static String generateVersionTableIdCriteria(Boolean scheduled) {
        return SqlBuilder.select(StrUtil.format("concat({}, '-', {})", TbAppFlinkJobConfig.ID_A, TbAppCollectTableInfo.ALIAS_A))
                .from(
                        TbAppCollectTableVersion._alias_,
                        TbAppFlinkJobConfig._alias_,
                        TbAppCollectTableInfo._alias_
                )
                .whereEq(TbAppCollectTableVersion.SCHEDULED_A, scheduled)
                .andEq(TbAppCollectTableVersion.FLINK_JOB_ID_A, Column.as(TbAppFlinkJobConfig.ID_A))
                .andEq(TbAppCollectTableVersion.ALIAS_A, Column.as(TbAppCollectTableInfo.ALIAS_A))
                .andEq(TbAppCollectTableVersion.VERSION_A, Column.as("date_format(subdate(current_date(), 1), '%Y%m%d')"))
                .andEq(TbAppFlinkJobConfig.STATUS_A, STATUS_Y)
                .andEq(TbAppCollectTableInfo.STATUS_A, STATUS_Y)
                .build();
    }

    private SqlBuilder generateUnScheduledNormalTableSql(SelectSqlBuilder builder, String version) {
        return builder
                .from(TbAppFlinkJobConfig._alias_, TbAppCollectTableInfo._alias_, TbAppCollectTableVersion._alias_)
                .whereEq(TbAppFlinkJobConfig.ID_A, Column.as(TbAppCollectTableInfo.FLINK_JOB_ID_A))
                .andEq(TbAppFlinkJobConfig.STATUS_A, "y")
                .andEq(TbAppCollectTableInfo.STATUS_A, "y")
                .andEq(TbAppFlinkJobConfig.ID_A, Column.as(TbAppCollectTableVersion.FLINK_JOB_ID_A))
                .andEq(TbAppCollectTableInfo.ALIAS_A, Column.as(TbAppCollectTableVersion.ALIAS_A))
                .andEq(TbAppCollectTableInfo.PRIORITY_A, 10000)
                .andEq(TbAppCollectTableInfo.SCHEDULE_ID_A, false)
                .andEq(TbAppCollectTableVersion.VERSION_A, "2018");
    }

    @Cacheable(value = "un-scheduled-normal-table", sync = true)
    @Retryable(Throwable.class)
    public ImmutableList<JobIdAndAlias> unScheduledNormalTable(String version) {
        return Lists.immutable.ofAll(
                mysqlJdbcTemplate.query(
                        generateUnScheduledNormalTableSql(SqlBuilder.select(TbAppCollectTableInfo.FLINK_JOB_ID_A, TbAppCollectTableInfo.ALIAS_A), version)
                                .build(),
                        (rs, row) -> new JobIdAndAlias(rs.getLong(1), rs.getString(2))
                )
        );
    }

    @Cacheable(value = "un_scheduled_normal_table_count", sync = true)
    @Retryable(Throwable.class)
    public Long unScheduledNormalTableCount(String version) {
        return mysqlJdbcTemplate.queryForObject(
                generateUnScheduledNormalTableSql(SqlBuilder.select(COUNT), version)
                        .build(),
                Long.class
        );
    }

    private SqlBuilder generateUnScheduledFocusTableSql(SelectSqlBuilder builder, String version) {
        return builder
                .from(TbAppCollectTableInfo._alias_)
                .join(TbAppCollectTableVersion._alias_)
                .onEq(TbAppCollectTableInfo.FLINK_JOB_ID_A, Column.as(TbAppCollectTableVersion.FLINK_JOB_ID_A))
                .andEq(TbAppCollectTableInfo.ALIAS_A, Column.as(TbAppCollectTableVersion.ALIAS_A))
                .whereGe(TbAppCollectTableInfo.PRIORITY_A, 10000)
                .andEq(TbAppCollectTableVersion.SCHEDULED_A, false)
                .andEq(TbAppCollectTableVersion.VERSION_A, version)
                .andEq(TbAppCollectTableInfo.STATUS_A, STATUS_Y);
    }

    @Cacheable(value = "un-scheduled-focus-table", sync = true)
    @Retryable(Throwable.class)
    public ImmutableList<JobIdAndAlias> unScheduledFocusTable(String version) {
        return Lists.immutable.ofAll(
                mysqlJdbcTemplate.query(
                        generateUnScheduledFocusTableSql(SqlBuilder.select(TbAppCollectTableInfo.FLINK_JOB_ID_A, TbAppCollectTableInfo.ALIAS_A), version)
                                .build(),
                        (rs, row) -> new JobIdAndAlias(rs.getLong(1), rs.getString(2))
                )
        );
    }

    @Cacheable(value = "un_scheduled_focus_table_count", sync = true)
    @Retryable(Throwable.class)
    public Long unScheduledFocusTableCount(String version) {
        return mysqlJdbcTemplate.queryForObject(
                generateUnScheduledFocusTableSql(SqlBuilder.select(COUNT), version)
                        .build(),
                Long.class
        );
    }

    @Cacheable("un-updated-version-table")
    @Retryable(Throwable.class)
    public ImmutableList<String> nonUpdatedVersionTables() {
        return mysqlTransactionTemplate.execute(status -> {
            String listSQL = generateVersionTableIdCriteria(false);
            sqlLogger.log(listSQL, "nonUpdatedVersionTables");
            List<String> ids = mysqlJdbcTemplate.queryForList(listSQL, String.class);
            return Lists.immutable.ofAll(ids);
        });
    }

    @Cacheable("updated-version-table")
    @Retryable(Throwable.class)
    public ImmutableList<String> updatedVersionTables() {
        return mysqlTransactionTemplate.execute(status -> {
            String listSQL = generateVersionTableIdCriteria(true);
            sqlLogger.log(listSQL, "updatedVersionTables");
            List<String> ids = mysqlJdbcTemplate.queryForList(listSQL, String.class);
            return Lists.immutable.ofAll(ids);
        });
    }

    private SqlBuilder generateVersionTableCriteria(
            SelectSqlBuilder builder,
            Integer page,
            Integer count,
            String version,
            Long flinkJobId,
            String alias,
            String order,
            String direction,
            ImmutableList<Boolean> filterSchedules,
            boolean limited,
            boolean groupBy
    ) {
        int limit = Math.max(count, 1);
        int offset = limit * Math.max(page - 1, 0);
        WhereSqlBuilder root = builder
                .from(
                        TbAppFlinkJobConfig._alias_,
                        TbAppCollectTableInfo._alias_,
                        TbAppCollectTableVersion._alias_,
                        TbAppHudiSyncState._alias_
                )
                .whereEq(TbAppFlinkJobConfig.ID_A, Column.as(TbAppCollectTableInfo.FLINK_JOB_ID_A))
                .andEq(TbAppFlinkJobConfig.STATUS_A, "y")
                .andEq(TbAppCollectTableInfo.STATUS_A, "y")
                .andEq(TbAppCollectTableVersion.FLINK_JOB_ID_A, Column.as(TbAppFlinkJobConfig.ID_A))
                .andEq(TbAppCollectTableVersion.ALIAS_A, Column.as(TbAppCollectTableInfo.ALIAS_A))
                .andEq(TbAppHudiSyncState.ID_A, Column.as(StrUtil.format("CONCAT({}, '-', {})", TbAppFlinkJobConfig.ID_A, TbAppCollectTableInfo.ALIAS_A)))
                .andLike(ObjectUtil.isNotNull(flinkJobId), TbAppCollectTableInfo.FLINK_JOB_ID_A, flinkJobId)
                .andLike(StrUtil.isNotBlank(alias), TbAppCollectTableInfo.ALIAS_A, alias)
                .andEq(StrUtil.isNotBlank(version), TbAppCollectTableVersion.VERSION_A, version)
                .andIn(ObjectUtil.isNotEmpty(filterSchedules), TbAppCollectTableVersion.SCHEDULED_A, filterSchedules);
        if (groupBy) {
            return root.groupBy(TbAppCollectTableVersion.SCHEDULED_A);
        } else {
            return root
                    .orderBy(StrUtil.isNotBlank(order) && StrUtil.isNotBlank(direction), () -> StrUtil.format("{} {}", order, direction))
                    .limit(limited, offset, count);
        }
    }

    @Cacheable(value = "version-tables", sync = true)
    @Retryable(Throwable.class)
    public PageResponse<VersionUpdated> findAllVersionTables(
            Integer page,
            Integer count,
            String version,
            Long flinkJobId,
            String alias,
            String order,
            String direction,
            ImmutableList<Boolean> filterSchedules
    ) {
        return mysqlTransactionTemplate.execute(status -> {
            Long total = mysqlJdbcTemplate.queryForObject(
                    generateVersionTableCriteria(
                            SqlBuilder.select(COUNT),
                            page,
                            count,
                            version,
                            flinkJobId,
                            alias,
                            order,
                            direction,
                            filterSchedules,
                            false,
                            false
                    ).build(),
                    Long.class
            );
            List<Pair<Boolean, Integer>> groupMap = mysqlJdbcTemplate.query(
                    generateVersionTableCriteria(
                            SqlBuilder.select(TbAppCollectTableVersion.SCHEDULED_A, COUNT),
                            page,
                            count,
                            version,
                            flinkJobId,
                            alias,
                            order,
                            direction,
                            filterSchedules,
                            false,
                            true
                    ).build(),
                    (rs, row) -> new Pair<>(rs.getBoolean(1), rs.getInt(2))
            );
            ImmutableMap<Boolean, Integer> scheduleCount = Lists.immutable.ofAll(groupMap)
                    .groupBy(Pair::getKey)
                    .toMap()
                    .collectValues((key, list) -> list.getOnly().getValue())
                    .toImmutable();
            String listSQL = generateVersionTableCriteria(
                    SqlBuilder.select(
                            TbAppFlinkJobConfig.ID_A,
                            TbAppCollectTableInfo.ALIAS_A,
                            TbAppCollectTableVersion.VERSION_A,
                            TbAppCollectTableVersion.SCHEDULED_A
                    ),
                    page,
                    count,
                    version,
                    flinkJobId,
                    alias,
                    order,
                    direction,
                    filterSchedules,
                    true,
                    false
            ).build();
            sqlLogger.log(listSQL, "findAllVersionTables");
            List<VersionUpdated> list = mysqlJdbcTemplate.query(
                    listSQL,
                    (rs, row) -> new VersionUpdated(
                            rs.getLong(1),
                            rs.getString(2),
                            rs.getString(3),
                            rs.getBoolean(4)
                    )
            );
            return new PageResponse<>(list, total)
                    .withMetadata("scheduled", scheduleCount.getOrDefault(true, 0))
                    .withMetadata("unScheduled", scheduleCount.getOrDefault(false, 0));
        });
    }

    private SqlBuilder generateUnReceiveVersionNormalTableSql(SelectSqlBuilder builder, String version) {
        return builder
                .from(TbAppFlinkJobConfig._alias_, TbAppCollectTableInfo._alias_)
                .whereLt(TbAppCollectTableInfo.PRIORITY_A, 10000)
                .andEq(TbAppCollectTableInfo.STATUS_A, STATUS_Y)
                .andEq(TbAppFlinkJobConfig.ID_A, Column.as(TbAppCollectTableInfo.FLINK_JOB_ID_A))
                .andEq(TbAppFlinkJobConfig.STATUS_A, STATUS_Y)
                .andNotIn(
                        StrUtil.format("concat({}, {})", TbAppFlinkJobConfig.ID_A, TbAppCollectTableInfo.ALIAS_A),
                        SqlBuilder.select(StrUtil.format("concat({}, {})", TbAppCollectTableVersion.FLINK_JOB_ID_A, TbAppCollectTableVersion.ALIAS_A))
                                .from(TbAppCollectTableVersion._alias_)
                                .whereEq(TbAppCollectTableVersion.VERSION_A, 1)
                );
    }

    @Cacheable(value = "un-receive-version-normal-table", sync = true)
    @Retryable(Throwable.class)
    public ImmutableList<JobIdAndAlias> unReceiveVersionNormalTable(String version) {
        return Lists.immutable.ofAll(
                mysqlJdbcTemplate.query(
                        generateUnReceiveVersionNormalTableSql(SqlBuilder.select(TbAppCollectTableInfo.FLINK_JOB_ID_A, TbAppCollectTableInfo.ALIAS_A), version)
                                .build(),
                        (rs, row) -> new JobIdAndAlias(rs.getLong(1), rs.getString(2))
                )
        );
    }

    @Cacheable(value = "un-receive-version-normal-table-count", sync = true)
    @Retryable(Throwable.class)
    public Long unReceiveVersionNormalTableCount(String version) {
        return mysqlJdbcTemplate.queryForObject(
                generateUnReceiveVersionNormalTableSql(SqlBuilder.select(COUNT), version)
                        .build(),
                Long.class
        );
    }

    private SqlBuilder generateUnReceiveVersionFocusTable(SelectSqlBuilder builder, String version) {
        return builder
                .from(TbAppFlinkJobConfig._alias_, TbAppCollectTableInfo._alias_)
                .whereGe(TbAppCollectTableInfo.PRIORITY_A, 10000)
                .andEq(TbAppCollectTableInfo.STATUS_A, "y")
                .andEq(TbAppFlinkJobConfig.ID_A, Column.as(TbAppCollectTableInfo.FLINK_JOB_ID_A))
                .andEq(TbAppFlinkJobConfig.STATUS_A, STATUS_Y)
                .andNotIn(
                        StrUtil.format("concat({}, {})", TbAppFlinkJobConfig.ID_A, TbAppCollectTableInfo.ALIAS_A),
                        SqlBuilder.select(StrUtil.format("concat({}, {})", TbAppCollectTableVersion.FLINK_JOB_ID_A, TbAppCollectTableVersion.ALIAS_A))
                                .from(TbAppCollectTableVersion._alias_)
                                .whereEq(TbAppCollectTableVersion.VERSION_A, version)
                );
    }

    @Cacheable(value = "un-receive-version-focus-table", sync = true)
    @Retryable(Throwable.class)
    public ImmutableList<JobIdAndAlias> unReceiveVersionFocusTable(String version) {
        return Lists.immutable.ofAll(
                mysqlJdbcTemplate.query(
                        generateUnReceiveVersionFocusTable(SqlBuilder.select(TbAppCollectTableInfo.FLINK_JOB_ID_A, TbAppCollectTableInfo.ALIAS_A), version)
                                .build(),
                        (rs, row) -> new JobIdAndAlias(rs.getLong(1), rs.getString(2))
                )
        );
    }

    @Cacheable(value = "un-receive-version-focus-table-count", sync = true)
    @Retryable(Throwable.class)
    public Long unReceiveVersionFocusTableCount(String version) {
        return mysqlJdbcTemplate.queryForObject(
                generateUnReceiveVersionFocusTable(SqlBuilder.select(COUNT), version)
                        .build(),
                Long.class
        );
    }
}
