package com.easyagents.federation.sql.api;

import com.easyagents.federation.sql.execute.QueryId;
import com.easyagents.federation.sql.execute.SqlExecutionOptions;
import com.easyagents.federation.sql.execute.SqlParameter;
import com.easyagents.federation.sql.federation.FederationQueryScopeDefinition;
import com.easyagents.federation.sql.federation.FederationSourceBindingDefinition;
import com.easyagents.federation.sql.source.SourceId;
import java.io.Serializable;
import java.time.Duration;
import java.util.List;

/**
 * 可持久化或跨节点传递的一体化查询命令。
 *
 * @param queryId 查询标识，可为空并在构造命令时生成
 * @param sql 单条只读 SQL
 * @param queryScope 查询可见的数据源范围
 * @param parameters 参数值
 * @param options JDBC 执行限制
 * @param admissionTimeoutMillis 查询准入等待毫秒数
 * @param policyVersion 策略版本
 */
public record SqlQueryCommand(
    QueryId queryId,
    String sql,
    FederationQueryScopeDefinition queryScope,
    List<SqlParameter> parameters,
    SqlExecutionOptions options,
    long admissionTimeoutMillis,
    String policyVersion
) implements Serializable {

    /**
     * 校验并防御性复制查询命令。
     */
    public SqlQueryCommand {
        if (sql == null || sql.isBlank() || queryScope == null) {
            throw new IllegalArgumentException("sql and queryScope must be provided");
        }
        if (admissionTimeoutMillis < 0) {
            throw new IllegalArgumentException("timeout must not be negative");
        }
        queryId = queryId == null ? QueryId.create() : queryId;
        parameters = List.copyOf(parameters == null ? List.of() : parameters);
        options = options == null ? SqlExecutionOptions.defaults() : options;
        policyVersion = policyVersion == null || policyVersion.isBlank() ? "default" : policyVersion;
    }

    /**
     * 使用单物理数据源创建兼容查询命令。
     *
     * @param queryId 查询标识
     * @param sql 单条只读 SQL
     * @param sourceId 默认数据源
     * @param minimumRevision 最低数据源版本
     * @param parameters 参数值
     * @param options JDBC 执行限制
     * @param admissionTimeoutMillis 准入等待毫秒数
     * @param policyVersion 策略版本
     */
    public SqlQueryCommand(
        QueryId queryId,
        String sql,
        SourceId sourceId,
        long minimumRevision,
        List<SqlParameter> parameters,
        SqlExecutionOptions options,
        long admissionTimeoutMillis,
        String policyVersion
    ) {
        this(
            queryId,
            sql,
            FederationQueryScopeDefinition.single(sourceId, minimumRevision),
            parameters,
            options,
            admissionTimeoutMillis,
            policyVersion
        );
    }

    /**
     * 返回默认 Binding 的物理数据源，供单源调用方兼容读取。
     *
     * @return 默认物理数据源
     */
    public SourceId sourceId() {
        return defaultBinding().sourceId();
    }

    /**
     * 返回默认 Binding 的最低物理 Definition 版本。
     *
     * @return 最低版本
     */
    public long minimumRevision() {
        return defaultBinding().minimumRevision();
    }

    private FederationSourceBindingDefinition defaultBinding() {
        return queryScope.defaultBindingDefinition();
    }

    /**
     * 创建使用默认执行限制的查询命令。
     *
     * @param sql 单条只读 SQL
     * @param sourceId 数据源标识
     * @param minimumRevision 最低数据源版本
     * @param parameters 参数
     * @return 查询命令
     */
    public static SqlQueryCommand of(
        String sql,
        SourceId sourceId,
        long minimumRevision,
        List<SqlParameter> parameters
    ) {
        return new SqlQueryCommand(
            null,
            sql,
            sourceId,
            minimumRevision,
            parameters,
            SqlExecutionOptions.defaults(),
            Duration.ofSeconds(5).toMillis(),
            "default"
        );
    }

    /**
     * 创建使用默认执行限制的查询范围命令。
     *
     * @param sql 单条只读 SQL
     * @param queryScope 查询范围
     * @param parameters 参数
     * @return 查询命令
     */
    public static SqlQueryCommand of(
        String sql,
        FederationQueryScopeDefinition queryScope,
        List<SqlParameter> parameters
    ) {
        return new SqlQueryCommand(
            null,
            sql,
            queryScope,
            parameters,
            SqlExecutionOptions.defaults(),
            Duration.ofSeconds(5).toMillis(),
            "default"
        );
    }
}
