package com.aucma.base.support;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * 设备参数分表路由器
 */
@Component
public class DeviceParamTableRouter {

    private static final String BASE_TABLE_NAME = "BASE_DEVICE_PARAM_VAL";
    private static final String PARTITION_TABLE_PREFIX = "BASE_DEVICE_PARAM_VAL_";
    private static final DateTimeFormatter YM_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM");
    private static final Pattern SUFFIX_PATTERN = Pattern.compile("^\\d{6}$");

    private final JdbcTemplate jdbcTemplate;
    private final Set<String> confirmedTables = ConcurrentHashMap.newKeySet();

    @Autowired
    public DeviceParamTableRouter(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public boolean isOldDevice(String deviceCode) {
        return deviceCode != null && deviceCode.startsWith("OLD-");
    }

    public String resolveWriteTable(String deviceCode, Date collectTime) {
        if (isOldDevice(deviceCode)) {
            return BASE_TABLE_NAME;
        }
        String suffix = toSuffix(collectTime == null ? new Date() : collectTime);
        String tableName = buildPartitionTableName(suffix);
        ensureTableExists(tableName);
        return tableName;
    }

    public List<String> resolveReadTableSuffixes(Date startTime, Date endTime) {
        if (startTime == null || endTime == null) {
            return Collections.emptyList();
        }
        LocalDate startMonth = toLocalDate(startTime).withDayOfMonth(1);
        LocalDate endMonth = toLocalDate(endTime).withDayOfMonth(1);
        if (startMonth.isAfter(endMonth)) {
            LocalDate temp = startMonth;
            startMonth = endMonth;
            endMonth = temp;
        }

        List<String> suffixes = new ArrayList<>();
        LocalDate cursor = startMonth;
        while (!cursor.isAfter(endMonth)) {
            String suffix = YM_FORMATTER.format(cursor);
            if (tableExists(buildPartitionTableName(suffix))) {
                suffixes.add(suffix);
            }
            cursor = cursor.plusMonths(1);
        }
        return suffixes;
    }

    private LocalDate toLocalDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    private String toSuffix(Date collectTime) {
        // 为什么这样做：物理分表已按自然月完整年月命名，使用 yyyyMM 才能避免跨年份时表名歧义。
        return YM_FORMATTER.format(toLocalDate(collectTime));
    }

    private String buildPartitionTableName(String suffix) {
        validateSuffix(suffix);
        return PARTITION_TABLE_PREFIX + suffix;
    }

    private void validateSuffix(String suffix) {
        if (suffix == null || !SUFFIX_PATTERN.matcher(suffix).matches()) {
            throw new IllegalArgumentException("非法分表后缀: " + suffix);
        }
    }

    private void ensureTableExists(String tableName) {
        if (!tableExists(tableName)) {
            throw new IllegalArgumentException("采集月分表不存在: " + tableName);
        }
    }

    private boolean tableExists(String tableName) {
        String normalizedName = tableName.toUpperCase(Locale.ROOT);
        if (confirmedTables.contains(normalizedName)) {
            return true;
        }
        Integer count = jdbcTemplate.queryForObject(
                "SELECT COUNT(1) FROM USER_TABLES WHERE TABLE_NAME = ?",
                Integer.class,
                normalizedName
        );
        boolean exists = count != null && count > 0;
        if (exists) {
            confirmedTables.add(normalizedName);
        }
        return exists;
    }
}
