import { resolve } from "node:path";
import { normalizeMode } from "./normalize-mode.js";
/**
 * Map a PipelineRequest to a ResolvedConfig.
 *
 * This is the single mapping function shared between:
 * - API gateway (POST /v1/pipeline → PipelineRequest → ResolvedConfig)
 * - CLI --config (JSON file → PipelineRequest → ResolvedConfig)
 * - GitHub Actions (repository_dispatch payload → PipelineRequest → ResolvedConfig)
 *
 * Fields not present in PipelineRequest get sensible defaults.
 * Fields that only exist in ResolvedConfig (rootDir, promptfooUrl, etc.)
 * must be provided by the caller via the rootDir parameter.
 *
 * **Publish default:** When `jobId` is present (API-triggered evaluation),
 * `publish` defaults to `true` so the report is persisted to the Content
 * Lake and the job document gets a `reportId`. Callers can still override
 * with `publish: false`.
 */
export function mapRequestToConfig(request, rootDir) {
    // Normalize mode so downstream pipeline code only sees canonical names.
    // The API may receive legacy names ("baseline", "full") from older clients.
    const { mode, variant: normalizedVariant } = normalizeMode(request.mode ?? "full");
    // Explicit variant from request takes precedence over one derived from
    // legacy mode normalization. This supports the canonical form:
    //   { mode: "literacy", variant: "baseline" }
    // while preserving backward compatibility with:
    //   { mode: "baseline" } → normalizeMode → { mode: "literacy", variant: "baseline" }
    const variant = request.variant ?? normalizedVariant;
    // API-triggered evaluations (identified by jobId) default to publish: true.
    // Without this, the job's reportId is always null and GET /v1/reports/:id
    // has nothing to return.
    const publishDefault = !!request.jobId;
    return {
        rootDir,
        outputDir: resolve(rootDir, "results", "latest"),
        mode,
        variant,
        debug: mapDebug(request.debug),
        models: request.models,
        areas: request.areas,
        tasks: request.tasks,
        changedDocs: request.changedDocs,
        injectContextDocs: request.injectContextDocs,
        source: request.source,
        skipFetch: false,
        skipEval: false,
        compareEnabled: request.compare ?? false,
        compareThreshold: request.compareThreshold,
        compareBaseline: request.compareBaseline,
        compareBaselineReportId: request.compareBaselineReportId,
        gapAnalysisEnabled: request.gapAnalysis ?? true,
        publishEnabled: request.publish ?? publishDefault,
        publishTag: request.publishTag,
        noAutoScope: request.noAutoScope ?? false,
        noCache: request.noCache ?? false,
        noRemoteCache: request.noRemoteCache ?? false,
        graderContext: request.graderContext,
        graderReplications: request.graderReplications,
        borderlineReplications: request.borderlineReplications,
        urls: request.urls,
        headers: request.headers,
        allowedOrigins: request.allowedOrigins,
        searchMode: request.searchMode ?? "open",
        concurrency: request.concurrency,
        datasetOverride: request.dataset,
        projectIdOverride: request.projectId,
        perspectiveOverride: request.perspective,
        taskSourceType: mapTaskSourceType(request.taskSource?.type, request.taskMode),
        outputPath: undefined,
        promptfooUrl: undefined,
        studioOriginOverride: request.studioOrigin,
        // D0090 — the `--sanity-document` fetch-time override now survives the
        // wire. `configToSourceOverrides` reads `sanityDocumentArgs` and emits the
        // same `SourceOverrides.documentIds` the local path does, so a `--remote`
        // run applies the identical replace/append document overlay.
        sanityDocumentArgs: request.documentIds,
        sourceReportId: request.sourceReportId,
        beforeOption: undefined,
        repoTasksPath: request.taskSource?.repoTasksPath
            ? resolve(rootDir, request.taskSource.repoTasksPath)
            : undefined,
        // Opt-in external-task registration. Omitted resolves to undefined, which
        // the mirror gate treats as false — so existing callers are unaffected.
        registerTasks: request.registerTasks,
        callerGit: request.callerGit,
        callerEnvelope: buildCallerEnvelope(request),
        callback: request.callback,
        jobId: request.jobId,
        remote: false,
        apiUrl: "https://ailf-api.sanity.build",
        presets: request.presets,
    };
}
function mapDebug(debug) {
    if (debug === undefined || debug === false)
        return undefined;
    if (debug === true)
        return { enabled: true };
    return {
        enabled: debug.enabled ?? true,
        firstN: debug.firstN,
        pattern: debug.pattern,
        sample: debug.sample,
    };
}
/**
 * Collect the D0037 caller envelope fields from a PipelineRequest into a
 * single `callerEnvelope` object. Returns undefined when no envelope
 * fields were provided, so downstream consumers can short-circuit with
 * `config.callerEnvelope?.classification` etc.
 */
function buildCallerEnvelope(request) {
    const { classification, owner, executor, purpose, labels } = request;
    if (classification === undefined &&
        owner === undefined &&
        executor === undefined &&
        purpose === undefined &&
        labels === undefined) {
        return undefined;
    }
    return { classification, owner, executor, purpose, labels };
}
function mapTaskSourceType(taskSourceType, taskMode) {
    // `taskSource.type` is the canonical field; honor it first when set.
    if (taskSourceType === "repo")
        return "repo";
    if (taskSourceType === "content-lake")
        return "content-lake";
    // Fall back to the legacy `taskMode` field.
    if (taskMode === "content-lake")
        return "content-lake";
    // "inline" means the caller sent inline tasks that will be materialized
    // to a temp directory. Use "repo" to ensure ONLY those tasks are used
    // (no Content Lake merge).
    if (taskMode === "inline")
        return "repo";
    return undefined;
}
