/**
 * Bridge from legacy ResolvedOptions to the new AppContext.
 *
 * This mapping function exists during the Phase 3→7 migration. It converts
 * the old ResolvedOptions shape to ResolvedConfig, then delegates to
 * createAppContext() (the composition root).
 *
 * Once all commands construct ResolvedConfig directly (or use --config),
 * this bridge can be deleted.
 */
import { isLiteracyVariant } from "../_vendor/ailf-shared/index.js";
import { createAppContext } from "../composition-root.js";
import { tryLoadConfigFile } from "../pipeline/compiler/config-loader.js";
/**
 * Map legacy ResolvedOptions to ResolvedConfig.
 *
 * This is a mechanical mapping — every field in ResolvedConfig has a
 * corresponding field in ResolvedOptions. Fields that don't map directly
 * are derived (e.g., areas from areaOption).
 */
export function mapToResolvedConfig(opts, rootDir) {
    // `opts.variant` is a free-form string from CLI / config flags; narrow it
    // to the closed `LiteracyVariant` set so downstream consumers (the report
    // provenance derivation, in particular) never see a bogus string.
    // Unknown values silently drop to undefined — the legacy behavior — but a
    // narrowing surface is in place for the day we want to error here.
    const variant = isLiteracyVariant(opts.variant) ? opts.variant : undefined;
    return {
        rootDir,
        mode: opts.mode,
        variant,
        noAutoScope: opts.noAutoScope ?? false,
        debug: opts.debug,
        areas: opts.areaOption
            ?.split(",")
            .map((s) => s.trim())
            .filter(Boolean),
        tasks: opts.taskOption
            ?.split(",")
            .map((s) => s.trim())
            .filter(Boolean),
        tags: opts.tagOption,
        changedDocs: opts.changedDocsOption
            ?.split(",")
            .map((s) => s.trim())
            .filter(Boolean),
        source: opts.source,
        skipFetch: opts.skipFetch,
        skipEval: opts.skipEval,
        compareEnabled: opts.compareEnabled,
        compareThreshold: opts.compareThreshold,
        compareBaseline: opts.compareBaseline,
        gapAnalysisEnabled: opts.gapAnalysisEnabled,
        publishEnabled: opts.publishEnabled,
        publishTag: opts.publishTag,
        noCache: opts.noCache,
        noRemoteCache: opts.noRemoteCache,
        graderReplications: opts.graderReplications,
        borderlineReplications: opts.borderlineReplications,
        graderContext: opts.graderContext,
        outputDir: opts.outputDir,
        outputPath: opts.outputPath,
        urls: opts.urlArgs.length > 0 ? opts.urlArgs : undefined,
        headers: opts.headerArgs.length > 0
            ? Object.fromEntries(opts.headerArgs.map((h) => {
                const [key, ...rest] = h.split(":");
                return [key.trim(), rest.join(":").trim()];
            }))
            : undefined,
        allowedOrigins: opts.allowedOriginArgs.length > 0 ? opts.allowedOriginArgs : undefined,
        searchMode: opts.searchMode ?? "open",
        concurrency: opts.concurrency,
        promptfooUrl: opts.promptfooUrl,
        datasetOverride: opts.datasetOverride,
        projectIdOverride: opts.projectIdOverride,
        perspectiveOverride: opts.perspectiveOverride,
        studioOriginOverride: opts.studioOriginOverride,
        sanityDocumentArgs: opts.sanityDocumentArgs.length > 0 ? opts.sanityDocumentArgs : undefined,
        beforeOption: opts.beforeOption,
        taskSourceType: opts.taskSourceType,
        repoTasksPath: opts.repoTasksPath,
        reportStoreProjectId: opts.reportProjectId,
        reportStoreDataset: opts.reportDataset,
        remote: opts.remote ?? false,
        apiUrl: opts.apiUrl ?? "https://ailf-api.sanity.build",
        apiKey: opts.apiKey,
        artifactsDisabled: opts.artifactsDisabled,
        artifactsDir: opts.artifactsDir,
        artifactsDryRun: opts.artifactsDryRun,
        artifactsExclude: opts.artifactsExclude,
        artifactGcsBucket: process.env.AILF_GCS_ARTIFACT_BUCKET,
        artifactUpload: parseArtifactUploadEnv(process.env.AILF_ARTIFACT_UPLOAD),
    };
}
/**
 * Parse the AILF_ARTIFACT_UPLOAD env var into a tri-state.
 *
 * - "0" | "false" → false (force-disable)
 * - "1" | "true"  → true  (force-enable; still a no-op without credentials)
 * - unset | other → undefined (auto-detect from credentials)
 */
export function parseArtifactUploadEnv(value) {
    if (value === undefined)
        return undefined;
    const normalized = value.trim().toLowerCase();
    if (normalized === "0" || normalized === "false")
        return false;
    if (normalized === "1" || normalized === "true")
        return true;
    return undefined;
}
/**
 * Build an AppContext from legacy ResolvedOptions.
 *
 * Delegates to createAppContext() (composition root) after mapping
 * ResolvedOptions → ResolvedConfig.
 */
export function buildAppContext(opts, rootDir) {
    const config = mapToResolvedConfig(opts, rootDir);
    // Inject config-file-only values that don't come from CLI options.
    // evalBudgetMs lives on ModelsConfig, not CLI flags.
    const models = tryLoadConfigFile("models", rootDir);
    if (models?.data?.evalBudgetMs) {
        config.evalBudgetMs = models.data.evalBudgetMs;
    }
    return createAppContext(config);
}
