package com.easyagents.federation.sql.execute;

import java.io.Serializable;
import java.sql.Types;

/**
 * 可序列化边界内的 JDBC 标量参数值与显式类型。
 *
 * <p>默认 Adapter 将 {@link Types#OTHER} 解释为 UUID；厂商专有 OTHER 类型应由
 * 对应 Adapter 覆盖参数类型映射。</p>
 *
 * @param jdbcType {@link Types} 类型值
 * @param value 参数值
 */
public record SqlParameter(int jdbcType, Object value) implements Serializable {

    /**
     * 校验跨节点参数值属于稳定、无嵌套对象图的 JDBC 标量类型。
     */
    public SqlParameter {
        if (jdbcType == Types.NULL) {
            throw new IllegalArgumentException("an explicit JDBC type is required for NULL parameters");
        }
        if (value != null && !isSupportedScalar(value)) {
            throw new IllegalArgumentException(
                "SQL parameter value must be a supported serializable JDBC scalar"
            );
        }
    }

    /**
     * 根据常用 Java 值推断 JDBC 类型。
     *
     * @param value 参数值
     * @return 参数
     */
    public static SqlParameter of(Object value) {
        return new SqlParameter(inferType(value), value);
    }

    private static int inferType(Object value) {
        if (value == null) {
            throw new IllegalArgumentException("use new SqlParameter(jdbcType, null) for NULL values");
        }
        if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
            return Types.INTEGER;
        }
        if (value instanceof Long) {
            return Types.BIGINT;
        }
        if (value instanceof Float) {
            return Types.REAL;
        }
        if (value instanceof Double) {
            return Types.DOUBLE;
        }
        if (value instanceof java.math.BigDecimal || value instanceof java.math.BigInteger) {
            return Types.DECIMAL;
        }
        if (value instanceof Boolean) {
            return Types.BOOLEAN;
        }
        if (value instanceof java.sql.Date || value instanceof java.time.LocalDate) {
            return Types.DATE;
        }
        if (value instanceof java.sql.Time || value instanceof java.time.LocalTime) {
            return Types.TIME;
        }
        if (value instanceof java.time.OffsetTime) {
            return Types.TIME_WITH_TIMEZONE;
        }
        if (value instanceof java.time.OffsetDateTime) {
            return Types.TIMESTAMP_WITH_TIMEZONE;
        }
        if (value instanceof java.sql.Timestamp || value instanceof java.time.LocalDateTime
            || value instanceof java.time.Instant) {
            return Types.TIMESTAMP;
        }
        if (value instanceof byte[]) {
            return Types.VARBINARY;
        }
        if (value instanceof java.util.UUID) {
            return Types.OTHER;
        }
        return Types.VARCHAR;
    }

    private static boolean isSupportedScalar(Object value) {
        return value instanceof String
            || value instanceof Character
            || value instanceof Boolean
            || value instanceof Byte
            || value instanceof Short
            || value instanceof Integer
            || value instanceof Long
            || value instanceof Float
            || value instanceof Double
            || value instanceof java.math.BigDecimal
            || value instanceof java.math.BigInteger
            || value instanceof byte[]
            || value instanceof java.sql.Date
            || value instanceof java.sql.Time
            || value instanceof java.sql.Timestamp
            || value instanceof java.time.LocalDate
            || value instanceof java.time.LocalTime
            || value instanceof java.time.LocalDateTime
            || value instanceof java.time.OffsetTime
            || value instanceof java.time.OffsetDateTime
            || value instanceof java.time.Instant
            || value instanceof java.util.UUID;
    }
}
