/**
 * Pipeline step: fail fast when explicitly selected tasks are non-viable.
 *
 * When a run selects tasks by id (`--task` / `PipelineRequest.tasks`) against
 * the Content Lake, the runner reads them under the **published** perspective.
 * A task that exists only as an unpublished draft (or is archived) resolves to
 * zero rows, and without this step the failure only surfaces three steps later
 * at `generate-configs` with the ambiguous "No literacy tasks match the current
 * filters" — after a runner spin-up and a doc fetch.
 *
 * This step runs before fetch-docs and names each offending id with a precise,
 * actionable reason, so the abort happens up front and the persisted
 * `ailf.evalRequest.error` is something a user can act on.
 *
 * Scope: only explicit `--task` selection against a `content-lake` source. The
 * viability decision uses the same adapter + perspective the runner uses
 * (`ctx.taskSource.loadTasks`); the reason is enriched via a best-effort
 * drafts-visible probe. Area/tag-only selections keep the existing
 * generate-configs zero-task handling, which also remains as a backstop.
 */
import { classifyTaskViability, describeTaskViabilityReason, supportsViabilityProbe, } from "../../_vendor/ailf-core/index.js";
export class ValidateTaskViabilityStep {
    name = "validate-task-viability";
    check() {
        return [];
    }
    async execute(ctx) {
        // Dedupe so a repeated --task id can't double a reason line or inflate
        // the "all runnable" count.
        const requested = [...new Set(ctx.config.tasks ?? [])];
        // Guard: only explicit --task selection against the Content Lake. Other
        // sources have no publish-state concept, and area/tag-only selections keep
        // the existing zero-task handling.
        if (requested.length === 0 ||
            ctx.config.taskSourceType !== "content-lake") {
            return {
                status: "skipped",
                reason: requested.length === 0
                    ? "no explicitly selected tasks to validate"
                    : "task viability check applies to content-lake selections only",
            };
        }
        const start = Date.now();
        // Authoritative viability: the SAME adapter + perspective the runner uses.
        // Pass only the task filter so each named id is judged on its own — an
        // id that exists but is excluded by an area/tag intersection is not a
        // viability problem and is handled downstream.
        const loaded = await ctx.taskSource.loadTasks({ taskIds: requested });
        const present = new Set(loaded.map((t) => t.id));
        const missing = requested.filter((id) => !present.has(id));
        if (missing.length === 0) {
            return {
                durationMs: Date.now() - start,
                status: "success",
                summary: `All ${requested.length} selected task(s) are runnable`,
            };
        }
        // Enrich the reason via a best-effort drafts-visible probe. The probe never
        // changes which ids are non-viable — `missing` is already decided above.
        let states;
        if (supportsViabilityProbe(ctx.taskSource)) {
            try {
                states = await ctx.taskSource.probeTaskPublishState(missing);
            }
            catch (err) {
                ctx.logger.warn(`[${this.name}] viability probe failed; reporting without per-id reasons: ${err instanceof Error ? err.message : String(err)}`);
            }
        }
        const lines = missing.map((id) => {
            const state = states?.get(id);
            const why = state
                ? reasonText(state)
                : "not runnable under the runner's published perspective (no published version — likely an unpublished draft, an archived task, or an unknown id)";
            return `  • ${id}: ${why}`;
        });
        return {
            durationMs: Date.now() - start,
            error: `${missing.length} selected task(s) cannot be evaluated by the runner:\n` +
                `${lines.join("\n")}\n` +
                "Publish or fix these tasks in Studio, or remove them from the selection. " +
                "The runner reads the Content Lake under the published perspective, so " +
                "unpublished drafts and archived tasks are never visible to it.",
            status: "failed",
        };
    }
}
/** Describe a non-viable publish state; viable states never reach here. */
function reasonText(state) {
    const result = classifyTaskViability(state);
    return result.viable
        ? "no published version visible to the runner"
        : describeTaskViabilityReason(result.reason);
}
