"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeWorkflowCommandUpdate = serializeWorkflowCommandUpdate;
exports.serializeWorkflowCommandFromWorkflowState = serializeWorkflowCommandFromWorkflowState;
const branding_1 = require("../core/branding");
const issue_completion_1 = require("../config/issue-completion");
const comment_command_1 = require("./comment-command");
const issue_override_option_registry_1 = require("./issue-override-option-registry");
const template_bindings_1 = require("./template-bindings");
const scheduled_run_1 = require("./scheduled-run");
const types_1 = require("./types");
const workflow_option_normalizer_1 = require("./workflow-option-normalizer");
const workflow_command_option_specs_1 = require("./workflow-command-option-specs");
const WORKFLOW_COMMAND_TOP_LEVEL_ROWS = [
    {
        flag: trimSerializedWorkflowCommandFlag(workflow_command_option_specs_1.WORKFLOW_SELECTION_COMMAND_OPTION_SPEC),
        source: "workflowSelection",
        valueKind: "workflow-selection",
    },
    ...workflow_command_option_specs_1.SIDE_WORKFLOW_COMMAND_OPTION_SPECS.map((spec) => ({
        flag: trimSerializedWorkflowCommandFlag(spec),
        source: "workflowTemplateSelector",
        surface: spec.surface,
        valueKind: "template-selector",
    })),
    {
        flag: "",
        source: "schedule",
        valueKind: "schedule",
    },
    {
        flag: "",
        source: "scheduledRun",
        valueKind: "scheduled-run",
    },
    {
        flag: "",
        source: "firstRun",
        valueKind: "first-run",
    },
    {
        flag: "",
        source: "recurringAutoStop",
        valueKind: "recurring-auto-stop",
    },
];
const WORKFLOW_COMMAND_BASE_BRANCH_ROW = {
    flag: "base-branch",
    source: "baseBranch",
    valueKind: "base-branch",
};
const WORKFLOW_COMMAND_OVERRIDE_ROWS = issue_override_option_registry_1.ISSUE_OVERRIDE_OPTION_REGISTRY.flatMap((row) => [
    ...(row.flag === "foreign-approval-scope"
        ? [WORKFLOW_COMMAND_BASE_BRANCH_ROW]
        : []),
    {
        flag: row.flag,
        source: "configOverridePatch",
        path: row.path,
        valueKind: mapOverrideValueKind(row.flag, row.kind),
    },
]);
const WORKFLOW_COMMAND_SERIALIZER_ROWS = [
    ...WORKFLOW_COMMAND_TOP_LEVEL_ROWS,
    ...WORKFLOW_COMMAND_OVERRIDE_ROWS,
];
function trimSerializedWorkflowCommandFlag(spec) {
    return spec.flag.slice(2);
}
function findSerializedWorkflowCommandOptionFlag(predicate) {
    const spec = workflow_command_option_specs_1.WORKFLOW_COMMAND_OPTION_SPECS.find(predicate);
    if (spec === undefined || !spec.flag.startsWith("--")) {
        throw new Error("Missing serialized workflow command option spec");
    }
    return spec.flag;
}
function findScheduleCommandFlag(mode) {
    return findSerializedWorkflowCommandOptionFlag((spec) => spec.kind === "schedule" && spec.mode === mode);
}
function findScheduledRunCommandFlag(mode) {
    return findSerializedWorkflowCommandOptionFlag((spec) => spec.kind === "scheduled-run" && spec.mode === mode);
}
function findFirstRunCommandFlag(mode) {
    return findSerializedWorkflowCommandOptionFlag((spec) => spec.kind === "first-run" && spec.mode === mode);
}
function findRecurringAutoStopCommandFlag(target) {
    return findSerializedWorkflowCommandOptionFlag((spec) => spec.kind === "recurring-auto-stop" && spec.target === target);
}
function findScheduleOffCommandFlag() {
    return findSerializedWorkflowCommandOptionFlag((spec) => spec.kind === "schedule-off");
}
function serializeWorkflowCommandUpdate(workflowOptions) {
    if (workflowOptions === undefined) {
        return null;
    }
    const overrideEntriesByPath = new Map(workflowOptions.configOverridePatch?.entries.map((entry) => [
        entry.path,
        entry,
    ]) ?? []);
    const tokens = WORKFLOW_COMMAND_SERIALIZER_ROWS.flatMap((row) => serializeWorkflowCommandRow(row, workflowOptions, overrideEntriesByPath));
    return tokens.length === 0
        ? null
        : [branding_1.ISSUE_COMMENT_COMMAND_PREFIX, ...tokens].join(" ");
}
function serializeWorkflowCommandFromWorkflowState(workflowState) {
    try {
        const workflowOptions = buildWorkflowCommandUpdateFieldsFromWorkflowState(workflowState);
        const command = serializeWorkflowCommandUpdate(workflowOptions);
        if (command === null) {
            return null;
        }
        return isSerializedWorkflowCommandLossless(command, workflowOptions)
            ? command
            : null;
    }
    catch {
        return null;
    }
}
function buildWorkflowCommandUpdateFieldsFromWorkflowState(workflowState) {
    const workflowTemplateSelectors = filterSerializableWorkflowTemplateSelectors((0, template_bindings_1.buildSideWorkflowTemplateSelectors)({
        workflowTemplates: workflowState.workflowTemplates,
        excludedSurface: workflowState.workflowSurface,
        includeImplicitAutoSelectors: true,
    }));
    const configOverridePatch = workflowState.configOverrides;
    const schedule = workflowState.schedule;
    const scheduledRun = workflowState.scheduledRun;
    const recurringAutoStop = workflowState.recurring?.autoStop;
    const workflowOptions = {
        workflowSelection: (0, template_bindings_1.getActiveWorkflowTemplateBinding)(workflowState)?.selector ??
            (0, types_1.createAutoSelectWorkflowTemplateSelector)(),
    };
    if (workflowTemplateSelectors !== undefined) {
        workflowOptions.workflowTemplateSelectors = workflowTemplateSelectors;
    }
    if (schedule !== undefined && schedule.mode !== "off") {
        workflowOptions.schedule = schedule;
    }
    if (scheduledRun !== undefined &&
        !(0, scheduled_run_1.isCooldownScheduledRunState)(scheduledRun)) {
        workflowOptions.scheduledRun = {
            mode: "run_at",
            value: scheduledRun.runAt,
        };
    }
    if (recurringAutoStop !== undefined) {
        workflowOptions.recurringAutoStop = {
            mode: "update",
            ...(recurringAutoStop.stopAt !== null
                ? { stopAt: recurringAutoStop.stopAt }
                : {}),
            ...(recurringAutoStop.stopAfterRuns !== null
                ? { stopAfterRuns: recurringAutoStop.stopAfterRuns }
                : {}),
        };
    }
    workflowOptions.baseBranch = {
        mode: "set",
        branch: workflowState.baseBranch,
    };
    if (configOverridePatch !== undefined &&
        configOverridePatch.entries.length > 0) {
        workflowOptions.configOverridePatch = configOverridePatch;
    }
    return workflowOptions;
}
function filterSerializableWorkflowTemplateSelectors(selectors) {
    const serializable = Object.fromEntries(workflow_command_option_specs_1.SIDE_WORKFLOW_COMMAND_OPTION_SPECS.flatMap((spec) => {
        const selector = selectors[spec.surface];
        return selector === undefined ? [] : [[spec.surface, selector]];
    }));
    return Object.keys(serializable).length > 0 ? serializable : undefined;
}
function isSerializedWorkflowCommandLossless(command, workflowOptions) {
    const parsed = (0, comment_command_1.parseWorkflowCommandComment)(command);
    return (parsed.kind === "update" &&
        stableStringify(normalizeWorkflowCommandUpdateFields((0, workflow_option_normalizer_1.pickWorkflowCommandUpdateFields)(parsed))) === stableStringify(normalizeWorkflowCommandUpdateFields(workflowOptions)));
}
function normalizeWorkflowCommandUpdateFields(workflowOptions) {
    if (workflowOptions.configOverridePatch === undefined) {
        return workflowOptions;
    }
    return {
        ...workflowOptions,
        configOverridePatch: {
            entries: [...workflowOptions.configOverridePatch.entries].sort(compareConfigOverrideEntries),
        },
    };
}
function compareConfigOverrideEntries(left, right) {
    if (left.path !== right.path) {
        return left.path.localeCompare(right.path);
    }
    return stableStringify(left).localeCompare(stableStringify(right));
}
function serializeWorkflowCommandRow(row, workflowOptions, overrideEntriesByPath) {
    switch (row.source) {
        case "workflowSelection":
            return serializeWorkflowSelectionRow(row.flag, workflowOptions);
        case "workflowTemplateSelector":
            return serializeTemplateSelectorRow(row.flag, workflowOptions.workflowTemplateSelectors?.[row.surface]);
        case "schedule":
            return serializeScheduleRow(workflowOptions.schedule);
        case "scheduledRun":
            return serializeScheduledRunRow(workflowOptions.scheduledRun);
        case "firstRun":
            return serializeFirstRunRow(workflowOptions.firstRun);
        case "recurringAutoStop":
            return serializeRecurringAutoStopRow(workflowOptions.recurringAutoStop);
        case "baseBranch":
            return serializeBaseBranchRow(row.flag, workflowOptions.baseBranch);
        case "configOverridePatch": {
            const entry = overrideEntriesByPath.get(row.path);
            if (entry === undefined) {
                return [];
            }
            return serializeOverrideRow(row, entry);
        }
    }
}
function serializeBaseBranchRow(flag, baseBranch) {
    if (baseBranch === undefined) {
        return [];
    }
    return [
        `--${flag}`,
        baseBranch.mode === "default"
            ? types_1.CONFIG_OVERRIDE_INHERIT_SENTINEL
            : formatWorkflowCommandToken(baseBranch.branch),
    ];
}
function serializeScheduledRunRow(scheduledRun) {
    if (scheduledRun === undefined || scheduledRun.mode === "off") {
        return [];
    }
    return [
        findScheduledRunCommandFlag(scheduledRun.mode),
        formatWorkflowCommandToken(scheduledRun.value),
    ];
}
function serializeFirstRunRow(firstRun) {
    if (firstRun === undefined) {
        return [];
    }
    if (firstRun.mode === "now") {
        return [findFirstRunCommandFlag(firstRun.mode)];
    }
    return [
        findFirstRunCommandFlag(firstRun.mode),
        formatWorkflowCommandToken(firstRun.value),
    ];
}
function serializeRecurringAutoStopRow(recurringAutoStop) {
    if (recurringAutoStop === undefined) {
        return [];
    }
    if (recurringAutoStop.mode === "off") {
        return [findRecurringAutoStopCommandFlag("off")];
    }
    return [
        ...(recurringAutoStop.stopAt !== undefined
            ? [
                findRecurringAutoStopCommandFlag("stopAt"),
                formatWorkflowCommandToken(recurringAutoStop.stopAt),
            ]
            : []),
        ...(recurringAutoStop.stopAfterRuns !== undefined
            ? [
                findRecurringAutoStopCommandFlag("stopAfterRuns"),
                String(recurringAutoStop.stopAfterRuns),
            ]
            : []),
    ];
}
function serializeWorkflowSelectionRow(flag, workflowOptions) {
    const selection = workflowOptions.workflowSelection;
    if (selection === undefined) {
        return [];
    }
    return [`--${flag}`, formatWorkflowTemplateSelector(selection)];
}
function serializeTemplateSelectorRow(flag, value) {
    if (value === undefined) {
        return [];
    }
    return [`--${flag}`, formatWorkflowTemplateSelector(value)];
}
function formatWorkflowTemplateSelector(selector) {
    switch (selector.mode) {
        case "auto":
            return types_1.WORKFLOW_TEMPLATE_AUTO_SENTINEL;
        case "auto_select":
            return types_1.WORKFLOW_TEMPLATE_AUTO_SELECT_SENTINEL;
        case "no_template":
            return types_1.WORKFLOW_TEMPLATE_NO_TEMPLATE_SENTINEL;
        case "template":
            return formatWorkflowCommandToken(selector.templatePath);
    }
}
function serializeScheduleRow(schedule) {
    if (schedule === undefined) {
        return [];
    }
    if (schedule.mode === "off") {
        return [findScheduleOffCommandFlag()];
    }
    return [
        findScheduleCommandFlag(schedule.mode),
        formatWorkflowCommandToken(schedule.value),
    ];
}
function serializeOverrideRow(row, entry) {
    switch (row.valueKind) {
        case "boolean":
            return [`--${row.flag}`, formatBooleanOverrideEntry(entry)];
        case "integer":
            return [`--${row.flag}`, formatScalarOverrideEntry(entry)];
        case "number":
            return [`--${row.flag}`, formatScalarOverrideEntry(entry)];
        case "enum":
            return [`--${row.flag}`, formatScalarOverrideEntry(entry)];
        case "string":
            return [`--${row.flag}`, formatScalarOverrideEntry(entry)];
        case "string-array":
            return [`--${row.flag}`, formatStringArrayOverrideEntry(entry)];
        case "workflow-template-list":
            return [`--${row.flag}`, formatStringArrayOverrideEntry(entry)];
        case "worker":
            return [`--${row.flag}`, formatWorkerOverrideEntry(entry)];
        default:
            return [];
    }
}
function formatBooleanOverrideEntry(entry) {
    if (entry.op === "inherit") {
        return types_1.CONFIG_OVERRIDE_INHERIT_SENTINEL;
    }
    if (entry.op === "set" && typeof entry.value === "boolean") {
        return String(entry.value);
    }
    throw new Error(`Unsupported boolean override for workflow command serialization: ${JSON.stringify(entry)}`);
}
function formatScalarOverrideEntry(entry) {
    if (entry.op === "inherit") {
        return types_1.CONFIG_OVERRIDE_INHERIT_SENTINEL;
    }
    if (entry.op === "disable") {
        return "off";
    }
    if (entry.op === "set") {
        const value = formatScalarOverrideValue(entry.path, entry.value);
        if (value === types_1.CONFIG_OVERRIDE_INHERIT_SENTINEL) {
            throw new Error(`Cannot serialize scalar override set to reserved sentinel: ${JSON.stringify(entry)}`);
        }
        return formatWorkflowCommandToken(value);
    }
    throw new Error(`Unsupported scalar override for workflow command serialization: ${JSON.stringify(entry)}`);
}
function formatScalarOverrideValue(path, value) {
    if (path === "issueCompletion.target" && (0, issue_completion_1.isIssueCompletionTarget)(value)) {
        return (0, issue_completion_1.formatIssueCompletionTargetForCli)(value);
    }
    if (path === "issueCompletion.pullRequest.reviewPolicy" &&
        (0, issue_completion_1.isPullRequestReviewPolicy)(value)) {
        return (0, issue_completion_1.formatPullRequestReviewPolicyForCli)(value);
    }
    if (path === "issueCompletion.pullRequest.mergePolicy" &&
        (0, issue_completion_1.isPullRequestMergePolicy)(value)) {
        return (0, issue_completion_1.formatPullRequestMergePolicyForCli)(value);
    }
    return String(value);
}
function formatStringArrayOverrideEntry(entry) {
    if (entry.op === "inherit") {
        return types_1.CONFIG_OVERRIDE_INHERIT_SENTINEL;
    }
    if (entry.op === "set" && Array.isArray(entry.value)) {
        return formatWorkflowCommandToken([...new Set(entry.value.map((value) => String(value)))].join(","));
    }
    throw new Error(`Unsupported string-array override for workflow command serialization: ${JSON.stringify(entry)}`);
}
function formatWorkerOverrideEntry(entry) {
    if (entry.op === "inherit") {
        return types_1.CONFIG_OVERRIDE_INHERIT_SENTINEL;
    }
    if (entry.op === "set" &&
        Array.isArray(entry.value) &&
        entry.value.length === 1 &&
        typeof entry.value[0] === "string") {
        return entry.value[0];
    }
    throw new Error(`Unsupported worker override for workflow command serialization: ${JSON.stringify(entry)}`);
}
function formatWorkflowCommandToken(value) {
    if (value.length === 0) {
        return '""';
    }
    if (!/\s/u.test(value)) {
        return value;
    }
    if (!value.includes('"')) {
        return `"${value}"`;
    }
    if (!value.includes("'")) {
        return `'${value}'`;
    }
    throw new Error(`Cannot serialize workflow command value containing both quote characters: ${JSON.stringify(value)}`);
}
function mapOverrideValueKind(_flag, kind) {
    switch (kind) {
        case "boolean":
            return "boolean";
        case "integer":
            return "integer";
        case "number":
            return "number";
        case "enum":
            return "enum";
        case "string":
            return "string";
        case "string-array":
            return "string-array";
        case "workflow-template-list":
            return "workflow-template-list";
        case "worker":
            return "worker";
    }
}
function stableStringify(value) {
    return JSON.stringify(sortJsonValue(value));
}
function sortJsonValue(value) {
    if (Array.isArray(value)) {
        return value.map((entry) => sortJsonValue(entry));
    }
    if (value === null || typeof value !== "object") {
        return value;
    }
    return Object.fromEntries(Object.entries(value)
        .sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey))
        .map(([key, entry]) => [key, sortJsonValue(entry)]));
}
