package com.easyagents.federation.sql.runtime;

import com.easyagents.federation.sql.adapter.AdapterCompatibility;
import com.easyagents.federation.sql.compile.FederationSqlPlan;
import com.easyagents.federation.sql.execute.FederationColumn;
import com.easyagents.federation.sql.federation.FederationFragmentPlan;
import com.easyagents.federation.sql.federation.FederationJoinOptimization;
import com.easyagents.federation.sql.federation.FederationQueryMode;
import com.easyagents.federation.sql.federation.FederationQueryScopeDefinition;
import com.easyagents.federation.sql.federation.FederationSourceRuntimeIdentity;
import com.easyagents.federation.sql.federation.FederationStatisticsSnapshot;
import com.easyagents.federation.sql.source.SourceId;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.runtime.Bindable;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlNode;

/**
 * Core 内部的计划实现。
 */
final class DefaultFederationSqlPlan implements FederationSqlPlan {

    private final SourceId sourceId;
    private final FederationQueryScopeDefinition queryScope;
    private final FederationQueryMode queryMode;
    private final long sourceRevision;
    private final String normalizedSql;
    private final String executableSql;
    private final SqlNode sqlNode;
    private final RelRoot relRoot;
    private final int parameterCount;
    private final List<Integer> parameterJdbcTypes;
    private final List<Integer> parameterMapping;
    private final List<FederationFragmentPlan> fragments;
    private final List<FederationJoinOptimization> joinOptimizations;
    private final List<FederationSourceRuntimeIdentity> sourceRuntimeIdentities;
    private final String scopeChecksum;
    private final List<FederationColumn> columns;
    private final Set<SourceId> referencedSources;
    private final AdapterCompatibility compatibility;
    private final boolean executable;
    private final String sourceChecksum;
    private final String adapterId;
    private final String runtimeFingerprint;
    private final Set<FederationStatisticsSnapshot.TableKey> statisticsTables;
    private final String statisticsFingerprint;
    private final Instant statisticsValidUntil;
    private final Map<String, CompiledFederationFragment> compiledFragments;
    private final String executionRelationalPlan;
    private final Bindable<?> localBindable;
    private final SchemaPlus localRootSchema;

    /**
     * 创建 Engine 内部计划。
     *
     * @param sourceId 主数据源
     * @param queryScope 查询范围
     * @param queryMode 查询模式
     * @param normalizedSql Calcite 规范化 SQL
     * @param executableSql 目标数据库参数化 SQL
     * @param sqlNode Calcite 已校验 SQL 节点
     * @param relRoot Calcite 关系计划
     * @param parameterCount 动态参数数量
     * @param parameterJdbcTypes 编译时声明的 JDBC 参数类型
     * @param parameterMapping 单源目标 SQL 占位符映射
     * @param fragments 物理分片
     * @param joinOptimizations 跨源 Join 优化选择
     * @param sourceRuntimeIdentities 实际引用源运行身份
     * @param columns 结果列
     * @param referencedSources 引用的数据源集合
     * @param compatibility Adapter 兼容性
     * @param executable 是否允许执行
     * @param sourceChecksum 数据源 Definition 校验和
     * @param adapterId Adapter 标识
     * @param compiledFragments 内部分片元数据
     * @param executionRelationalPlan 实际单源下推或联邦本地关系计划文本
     * @param localBindable 联邦本地可执行计划；单源为 null
     * @param localRootSchema 分片占位表根 Schema；单源为 null
     * @param statisticsTables 查询实际引用的统计表键
     * @param statisticsFingerprint 查询级统计指纹
     * @param statisticsValidUntil 查询级统计最早失效时间
     */
    DefaultFederationSqlPlan(
        FederationQueryScopeDefinition queryScope,
        FederationQueryMode queryMode,
        String normalizedSql,
        String executableSql,
        SqlNode sqlNode,
        RelRoot relRoot,
        int parameterCount,
        List<Integer> parameterJdbcTypes,
        List<Integer> parameterMapping,
        List<FederationFragmentPlan> fragments,
        List<FederationJoinOptimization> joinOptimizations,
        List<FederationSourceRuntimeIdentity> sourceRuntimeIdentities,
        List<FederationColumn> columns,
        Set<SourceId> referencedSources,
        AdapterCompatibility compatibility,
        boolean executable,
        Map<String, CompiledFederationFragment> compiledFragments,
        String executionRelationalPlan,
        Bindable<?> localBindable,
        SchemaPlus localRootSchema,
        Set<FederationStatisticsSnapshot.TableKey> statisticsTables,
        String statisticsFingerprint,
        Instant statisticsValidUntil
    ) {
        if (fragments == null || fragments.isEmpty()
            || sourceRuntimeIdentities == null || sourceRuntimeIdentities.isEmpty()) {
            throw new IllegalArgumentException("plan fragments and runtime identities must not be empty");
        }
        FederationSourceRuntimeIdentity primary = sourceRuntimeIdentities.stream()
            .filter(identity -> identity.bindingName().equals(queryScope.defaultBinding()))
            .findFirst()
            .orElse(sourceRuntimeIdentities.get(0));
        this.sourceId = primary.sourceId();
        this.sourceRevision = primary.sourceRevision();
        this.queryScope = queryScope;
        this.queryMode = queryMode;
        this.normalizedSql = normalizedSql;
        this.executableSql = executableSql;
        this.sqlNode = sqlNode;
        this.relRoot = relRoot;
        this.parameterCount = parameterCount;
        this.parameterJdbcTypes = List.copyOf(parameterJdbcTypes);
        this.parameterMapping = List.copyOf(parameterMapping);
        this.fragments = List.copyOf(fragments);
        this.joinOptimizations = List.copyOf(
            joinOptimizations == null ? List.of() : joinOptimizations
        );
        this.sourceRuntimeIdentities = List.copyOf(sourceRuntimeIdentities);
        this.scopeChecksum = queryScope.checksum();
        this.columns = List.copyOf(columns);
        this.referencedSources = Set.copyOf(referencedSources);
        this.compatibility = compatibility;
        this.executable = executable;
        this.sourceChecksum = primary.sourceChecksum();
        this.adapterId = primary.adapterId();
        this.runtimeFingerprint = primary.runtimeFingerprint();
        this.statisticsTables = Set.copyOf(
            statisticsTables == null ? Set.of() : statisticsTables
        );
        this.statisticsFingerprint = statisticsFingerprint == null
            ? ""
            : statisticsFingerprint;
        this.statisticsValidUntil = statisticsValidUntil == null
            ? Instant.MAX
            : statisticsValidUntil;
        this.compiledFragments = Map.copyOf(new LinkedHashMap<>(compiledFragments));
        this.executionRelationalPlan = executionRelationalPlan;
        this.localBindable = localBindable;
        this.localRootSchema = localRootSchema;
    }

    /**
     * 保留 Core 内部缓存测试使用的单源兼容构造器。
     */
    DefaultFederationSqlPlan(
        SourceId sourceId,
        long sourceRevision,
        String normalizedSql,
        String executableSql,
        SqlNode sqlNode,
        RelRoot relRoot,
        int parameterCount,
        List<Integer> parameterMapping,
        List<FederationColumn> columns,
        Set<SourceId> referencedSources,
        AdapterCompatibility compatibility,
        boolean executable,
        String sourceChecksum,
        String adapterId,
        String runtimeFingerprint
    ) {
        this(
            sourceId,
            sourceRevision,
            normalizedSql,
            executableSql,
            sqlNode,
            relRoot,
            parameterCount,
            parameterMapping,
            columns,
            referencedSources,
            compatibility,
            executable,
            sourceChecksum,
            adapterId,
            runtimeFingerprint,
            Instant.MAX
        );
    }

    /**
     * 保留 Core 内部统计缓存有效期测试使用的单源兼容构造器。
     *
     * @param sourceId 主数据源
     * @param sourceRevision 数据源版本
     * @param normalizedSql 规范化 SQL
     * @param executableSql 可执行 SQL
     * @param sqlNode SQL 节点
     * @param relRoot 关系计划
     * @param parameterCount 参数数量
     * @param parameterMapping 参数映射
     * @param columns 结果列
     * @param referencedSources 引用源
     * @param compatibility Adapter 兼容性
     * @param executable 是否可执行
     * @param sourceChecksum 源校验和
     * @param adapterId Adapter 标识
     * @param runtimeFingerprint 运行指纹
     * @param statisticsValidUntil 统计最早失效时间
     */
    DefaultFederationSqlPlan(
        SourceId sourceId,
        long sourceRevision,
        String normalizedSql,
        String executableSql,
        SqlNode sqlNode,
        RelRoot relRoot,
        int parameterCount,
        List<Integer> parameterMapping,
        List<FederationColumn> columns,
        Set<SourceId> referencedSources,
        AdapterCompatibility compatibility,
        boolean executable,
        String sourceChecksum,
        String adapterId,
        String runtimeFingerprint,
        Instant statisticsValidUntil
    ) {
        this(
            FederationQueryScopeDefinition.single(sourceId, sourceRevision),
            FederationQueryMode.SINGLE_SOURCE,
            normalizedSql,
            executableSql,
            sqlNode,
            relRoot,
            parameterCount,
            List.of(),
            parameterMapping,
            List.of(new FederationFragmentPlan(
                "fragment-1",
                sourceId.value(),
                sourceId,
                executableSql,
                parameterMapping,
                columns
            )),
            List.of(),
            List.of(new FederationSourceRuntimeIdentity(
                sourceId.value(),
                sourceId,
                sourceRevision,
                sourceChecksum,
                adapterId,
                runtimeFingerprint
            )),
            columns,
            referencedSources,
            compatibility,
            executable,
            Map.of(),
            "",
            null,
            null,
            Set.of(),
            "",
            statisticsValidUntil
        );
    }

    /**
     * 为一次公开 compile 调用创建独立签发实例，并共享不可变编译产物。
     *
     * @return 独立计划签发实例
     */
    DefaultFederationSqlPlan issuedCopy() {
        return new DefaultFederationSqlPlan(this);
    }

    /**
     * 复制缓存计划的不可变引用，避免热命中重复计算摘要或复制集合。
     *
     * @param cached 缓存中的编译计划
     */
    private DefaultFederationSqlPlan(DefaultFederationSqlPlan cached) {
        this.sourceId = cached.sourceId;
        this.queryScope = cached.queryScope;
        this.queryMode = cached.queryMode;
        this.sourceRevision = cached.sourceRevision;
        this.normalizedSql = cached.normalizedSql;
        this.executableSql = cached.executableSql;
        this.sqlNode = cached.sqlNode;
        this.relRoot = cached.relRoot;
        this.parameterCount = cached.parameterCount;
        this.parameterJdbcTypes = cached.parameterJdbcTypes;
        this.parameterMapping = cached.parameterMapping;
        this.fragments = cached.fragments;
        this.joinOptimizations = cached.joinOptimizations;
        this.sourceRuntimeIdentities = cached.sourceRuntimeIdentities;
        this.scopeChecksum = cached.scopeChecksum;
        this.columns = cached.columns;
        this.referencedSources = cached.referencedSources;
        this.compatibility = cached.compatibility;
        this.executable = cached.executable;
        this.sourceChecksum = cached.sourceChecksum;
        this.adapterId = cached.adapterId;
        this.runtimeFingerprint = cached.runtimeFingerprint;
        this.statisticsTables = cached.statisticsTables;
        this.statisticsFingerprint = cached.statisticsFingerprint;
        this.statisticsValidUntil = cached.statisticsValidUntil;
        this.compiledFragments = cached.compiledFragments;
        this.executionRelationalPlan = cached.executionRelationalPlan;
        this.localBindable = cached.localBindable;
        this.localRootSchema = cached.localRootSchema;
    }

    /** {@inheritDoc} */
    @Override
    public SourceId sourceId() {
        return sourceId;
    }

    /** {@inheritDoc} */
    @Override
    public FederationQueryScopeDefinition queryScope() {
        return queryScope;
    }

    /** {@inheritDoc} */
    @Override
    public FederationQueryMode queryMode() {
        return queryMode;
    }

    /** {@inheritDoc} */
    @Override
    public long sourceRevision() {
        return sourceRevision;
    }

    /** {@inheritDoc} */
    @Override
    public String normalizedSql() {
        return normalizedSql;
    }

    /** {@inheritDoc} */
    @Override
    public String executableSql() {
        return executableSql;
    }

    /** {@inheritDoc} */
    @Override
    public SqlNode sqlNode() {
        return sqlNode;
    }

    /** {@inheritDoc} */
    @Override
    public RelRoot relRoot() {
        return relRoot;
    }

    /** {@inheritDoc} */
    @Override
    public int parameterCount() {
        return parameterCount;
    }

    /** {@inheritDoc} */
    @Override
    public List<Integer> parameterJdbcTypes() {
        return parameterJdbcTypes;
    }

    /** {@inheritDoc} */
    @Override
    public List<Integer> parameterMapping() {
        return parameterMapping;
    }

    /** {@inheritDoc} */
    @Override
    public List<FederationFragmentPlan> fragments() {
        return fragments;
    }

    /** {@inheritDoc} */
    @Override
    public List<FederationJoinOptimization> joinOptimizations() {
        return joinOptimizations;
    }

    /** {@inheritDoc} */
    @Override
    public List<FederationSourceRuntimeIdentity> sourceRuntimeIdentities() {
        return sourceRuntimeIdentities;
    }

    /** {@inheritDoc} */
    @Override
    public String scopeChecksum() {
        return scopeChecksum;
    }

    /** {@inheritDoc} */
    @Override
    public List<FederationColumn> columns() {
        return columns;
    }

    /** {@inheritDoc} */
    @Override
    public Set<SourceId> referencedSources() {
        return referencedSources;
    }

    /** {@inheritDoc} */
    @Override
    public AdapterCompatibility compatibility() {
        return compatibility;
    }

    /** {@inheritDoc} */
    @Override
    public boolean executable() {
        return executable;
    }

    /** {@inheritDoc} */
    @Override
    public String sourceChecksum() {
        return sourceChecksum;
    }

    /** {@inheritDoc} */
    @Override
    public String adapterId() {
        return adapterId;
    }

    /** {@inheritDoc} */
    @Override
    public String runtimeFingerprint() {
        return runtimeFingerprint;
    }

    /** {@inheritDoc} */
    @Override
    public Instant statisticsValidUntil() {
        return statisticsValidUntil;
    }

    /**
     * 返回该计划实际引用的统计表键。
     *
     * @return 不可变统计表键集合
     */
    Set<FederationStatisticsSnapshot.TableKey> statisticsTables() {
        return statisticsTables;
    }

    /**
     * 返回编译时查询级统计指纹。
     *
     * @return 统计指纹
     */
    String statisticsFingerprint() {
        return statisticsFingerprint;
    }

    /**
     * 返回内部编译分片。
     *
     * @return 分片映射
     */
    Map<String, CompiledFederationFragment> compiledFragments() {
        return compiledFragments;
    }

    /**
     * 返回实际执行的关系计划文本。
     *
     * @return 单源下推或联邦本地计划
     */
    String executionRelationalPlan() {
        return executionRelationalPlan;
    }

    /**
     * 返回联邦本地 Bindable。
     *
     * @return Bindable；单源计划返回 null
     */
    Bindable<?> localBindable() {
        return localBindable;
    }

    /**
     * 返回联邦本地分片占位表根 Schema。
     *
     * @return 根 Schema；单源计划返回 null
     */
    SchemaPlus localRootSchema() {
        return localRootSchema;
    }
}
