package com.easyagents.federation.sql.adapter;

import com.easyagents.federation.sql.execute.FederationFragmentExecutor;
import com.easyagents.federation.sql.execute.FederationFragmentExplainer;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Optional;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.sql.SqlBasicTypeNameSpec;
import org.apache.calcite.sql.SqlDataTypeSpec;
import org.apache.calcite.sql.SqlDialect;
import org.apache.calcite.sql.SqlOperatorTable;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.type.SqlTypeName;

/**
 * 直接扩展 Calcite Schema、Dialect、类型和规则的数据库 Adapter SPI。
 */
public interface FederationSqlAdapterProvider {

    /**
     * 返回全局唯一 Adapter 标识。
     *
     * @return Adapter 标识
     */
    String adapterId();

    /**
     * 判断当前数据库与驱动是否受支持。
     *
     * @param metadata JDBC 元数据
     * @param hints 非敏感提示
     * @return 是否受支持
     * @throws SQLException 元数据读取失败
     */
    boolean supports(DatabaseMetaData metadata, AdapterHints hints) throws SQLException;

    /**
     * 返回当前数据库的兼容性证据状态。
     *
     * @param metadata JDBC 元数据
     * @param hints 非敏感提示
     * @return 兼容性说明
     * @throws SQLException 元数据读取失败
     */
    AdapterCompatibility compatibility(DatabaseMetaData metadata, AdapterHints hints) throws SQLException;

    /**
     * 创建当前 Definition 对应的 Calcite Schema。
     *
     * @param context Schema 上下文
     * @return Calcite Schema
     */
    Schema createSchema(AdapterSchemaContext context);

    /**
     * 选择目标数据库 SqlDialect。
     *
     * @param context 方言上下文
     * @return Calcite SqlDialect
     * @throws SQLException 元数据读取失败
     */
    SqlDialect createDialect(AdapterDialectContext context) throws SQLException;

    /**
     * 返回数据库类型系统。
     *
     * @return Calcite 类型系统
     */
    default RelDataTypeSystem typeSystem() {
        return RelDataTypeSystem.DEFAULT;
    }

    /**
     * 返回数据库运算符表。
     *
     * @return Calcite 运算符表
     */
    default SqlOperatorTable operatorTable() {
        return SqlStdOperatorTable.instance();
    }

    /**
     * 返回 Adapter 附加的 Calcite Planner 规则。
     *
     * @return Planner 规则
     */
    default List<RelOptRule> plannerRules() {
        return List.of();
    }

    /**
     * 创建保留 ANSI 双引号输入、继承目标方言大小写语义的解析配置。
     *
     * <p>数据库若对表名与列名采用不同的大小写规则，可以在 Adapter 中覆盖。</p>
     *
     * @param dialect 目标数据库方言
     * @return Calcite 解析配置
     */
    default SqlParser.Config parserConfig(SqlDialect dialect) {
        return SqlParser.config()
            .withQuotedCasing(dialect.getQuotedCasing())
            .withUnquotedCasing(dialect.getUnquotedCasing())
            .withCaseSensitive(dialect.isCaseSensitive());
    }

    /**
     * 将 JDBC 参数类型映射为 Calcite 类型声明，供动态参数参与校验和类型推导。
     *
     * <p>默认实现补齐 JDBC 4.2 时区类型，并将 {@link Types#OTHER} 解释为 UUID。
     * 厂商 Adapter 可以覆盖此方法，直接返回带精度、长度或专有类型名的
     * Calcite 类型声明。</p>
     *
     * @param jdbcType {@link java.sql.Types} 类型值
     * @param parserPosition 动态参数的解析位置
     * @return Calcite 类型声明；无法映射时返回 null
     */
    default SqlDataTypeSpec parameterTypeSpec(int jdbcType, SqlParserPos parserPosition) {
        SqlTypeName typeName = switch (jdbcType) {
            case Types.TIME_WITH_TIMEZONE -> SqlTypeName.TIME_TZ;
            case Types.TIMESTAMP_WITH_TIMEZONE -> SqlTypeName.TIMESTAMP_TZ;
            case Types.OTHER -> SqlTypeName.UUID;
            default -> SqlTypeName.getNameForJdbcType(jdbcType);
        };
        if (typeName == null || typeName.isSpecial() || !typeName.allowsNoPrecNoScale()) {
            return null;
        }
        return new SqlDataTypeSpec(
            new SqlBasicTypeNameSpec(typeName, parserPosition),
            parserPosition
        );
    }

    /**
     * 返回目标数据库 Fragment 执行器。
     *
     * @return Fragment 执行器
     */
    FederationFragmentExecutor fragmentExecutor();

    /**
     * 返回可选的数据库物理 Explain 实现。
     *
     * @return 物理 Explain SPI
     */
    default Optional<FederationFragmentExplainer> fragmentExplainer() {
        return Optional.empty();
    }

    /**
     * 返回可选的数据库目录统计采集器。
     *
     * <p>统计采集由引擎管理缓存、并发合并、失效和失败降级，Adapter 只负责
     * 当前数据库的目录语义。</p>
     *
     * @return 统计采集 SPI；未适配时为空
     */
    default Optional<FederationStatisticsCollector> statisticsCollector() {
        return Optional.empty();
    }
}
