"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatCanonicalWorkflowStateComment = formatCanonicalWorkflowStateComment;
exports.formatCanonicalWorkflowStateCommentForRepo = formatCanonicalWorkflowStateCommentForRepo;
// biome-ignore lint/nursery/noExcessiveLinesPerFile: Canonical workflow state rendering stays grouped for snapshot readability.
const catalog_1 = require("../../i18n/catalog");
const display_time_1 = require("../../i18n/display-time");
const active_command_1 = require("../../issues/active-command");
const approval_instructions_1 = require("../../workflows/approval-instructions");
const display_labels_1 = require("../../workflows/display-labels");
const scheduled_run_1 = require("../../workflows/scheduled-run");
const workflow_command_serializer_1 = require("../../workflows/workflow-command-serializer");
const workflow_contract_1 = require("../../workflows/workflow-contract");
const defs_1 = require("./defs");
const helpers_1 = require("./helpers");
const machine_state_1 = require("./machine-state");
const results_1 = require("./results");
const scheduling_1 = require("./scheduling");
const start_approval_1 = require("./start-approval");
const summary_1 = require("./summary");
const tree_1 = require("./tree");
function renderCanonicalWorkflowStateComment(workflowState, flavor = "gfm-collapsible", options) {
    const locale = options.locale ?? catalog_1.DEFAULT_APP_LOCALE;
    const displayTimeZone = options.displayTimeZone ?? "UTC";
    const labels = (0, defs_1.getCanonicalWorkflowLocaleLabels)(locale);
    const actionRequiredLines = (0, summary_1.selectActionRequiredLines)(workflowState);
    const displayTree = (0, tree_1.buildDisplayTree)(workflowState);
    const finalResultLines = shouldRenderFinalResult(workflowState)
        ? [
            `## ${labels.finalResultHeading}`,
            "",
            renderWorkflowResultSection(workflowState.finalResult, labels),
            "",
        ]
        : [];
    const lines = [
        ...renderGoalSection(workflowState, labels),
        ...finalResultLines,
        (0, defs_1.getCanonicalWorkflowStateHeading)(locale),
        "",
        ...renderSummarySection(workflowState, displayTree, normalizeNullableText(options.lastUpdated), labels, locale, displayTimeZone, options.issueId ?? null, options.workflowDisplayLabels),
    ];
    if (actionRequiredLines !== null && actionRequiredLines.length > 0) {
        lines.push("", `## ${labels.actionRequiredHeading}`, "", renderFreeformListSection(actionRequiredLines));
    }
    lines.push("", `## ${labels.workflowTreeHeading}`, "", renderWorkflowTree(workflowState, displayTree, labels, locale, displayTimeZone));
    const activeCommandSection = renderActiveCommandSection(workflowState, locale);
    lines.push("", `## ${labels.debugMetadataHeading}`, "", (0, machine_state_1.renderCanonicalWorkflowMachineStateSection)(workflowState, flavor, labels, activeCommandSection === null ? [] : [activeCommandSection]), "");
    return lines.join("\n");
}
function renderActiveCommandSection(workflowState, locale) {
    const command = (0, workflow_command_serializer_1.serializeWorkflowCommandFromWorkflowState)(workflowState);
    return command === null
        ? null
        : (0, active_command_1.renderIssueActiveCommandSection)(locale, command);
}
function formatCanonicalWorkflowStateComment(workflowState, flavor = "gfm-collapsible", options) {
    return renderCanonicalWorkflowStateComment(workflowState, flavor, options);
}
async function formatCanonicalWorkflowStateCommentForRepo(workflowState, repoRoot, options = {}) {
    const locale = options.locale ?? catalog_1.DEFAULT_APP_LOCALE;
    return formatCanonicalWorkflowStateComment(workflowState, options.flavor, {
        ...options,
        locale,
        workflowDisplayLabels: await (0, display_labels_1.resolveWorkflowDisplayLabelsForRepo)({
            repoRoot,
            locale,
            workflowTemplates: workflowState.workflowTemplates,
            activeSurface: workflowState.workflowSurface,
        }),
    });
}
function renderGoalSection(workflowState, labels) {
    const objective = (0, helpers_1.flattenMarkdownSummary)(workflowState.executionContract.objective);
    if (objective === null || objective === workflow_contract_1.WORKFLOW_DEFAULT_CONTRACT_OBJECTIVE) {
        return [];
    }
    return [labels.goalHeading, "", renderGoalValue(objective), ""];
}
function renderGoalValue(value) {
    return (0, helpers_1.renderValue)(value)
        .replace(/<!--/gu, "&lt;!--")
        .replace(/-->/gu, "--&gt;");
}
function renderSummarySection(workflowState, displayTree, lastUpdated, labels, locale, displayTimeZone, issueId, workflowDisplayLabels) {
    const isScheduledRun = (0, scheduling_1.isScheduledRunWorkflowState)(workflowState);
    const currentStepResult = (0, summary_1.selectCurrentStepResult)(workflowState);
    const currentLabel = renderSchedulingCurrentLabel(workflowState, labels) ??
        ((0, tree_1.isWorkflowPlanningInProgress)(workflowState)
            ? labels.currentStatusPlanningLabel
            : null) ??
        (displayTree.currentStepNode === null
            ? labels.currentStatusCurrentStepUnavailableLabel
            : `**${displayTree.currentStepNode.pathLabel} ${(0, helpers_1.renderWorkflowStepTitleWithIssueRef)(displayTree.currentStepNode.step)}**`);
    const nextAction = renderNextAction(workflowState, displayTree, labels, locale, displayTimeZone, issueId, currentStepResult);
    return [
        `- **${labels.currentStatusStateLabel}:** ${renderWorkflowStateLabel(workflowState, labels)}`,
        `- **${labels.currentStatusWorkflowLabel}:** ${workflowDisplayLabels.workflowTemplateLabel ?? (0, catalog_1.translate)(locale, "workflow.runContext.none")}`,
        `- **${labels.currentLabel}:** ${currentLabel}`,
        ...renderOptionalSummaryLine(labels.pathLabel, isScheduledRun ? null : displayTree.currentPath),
        ...renderOptionalSummaryLine(labels.topLevelProgressLabel, !isScheduledRun && displayTree.topLevelTotalCount > 0
            ? `\`${displayTree.topLevelDoneCount}/${displayTree.topLevelTotalCount}\``
            : null),
        ...renderOptionalSummaryLine(labels.currentStatusNextLabel, nextAction),
        ...renderOptionalSummaryLine(labels.followupStatusLabel, renderFollowupSummary(workflowState, labels, workflowDisplayLabels, locale)),
        ...renderOptionalSummaryLine(labels.currentStatusLastUpdatedLabel, lastUpdated
            ? (0, display_time_1.formatHumanTimestamp)(lastUpdated, locale, displayTimeZone)
            : null),
    ];
}
function renderOptionalSummaryLine(label, value) {
    return value === null ? [] : [`- **${label}:** ${value}`];
}
function renderFollowupSummary(workflowState, labels, workflowDisplayLabels, locale) {
    const followup = workflowState.followup;
    if (followup === undefined) {
        return renderFollowupConfigSummary({
            labels,
            proposalPolicy: "off",
            proposalApproval: "auto",
        });
    }
    const parts = [
        renderFollowupConfigSummary({
            labels,
            proposalPolicy: followup.proposalPolicy,
            proposalApproval: followup.proposalApproval,
        }),
    ];
    if (followup.proposalPolicy === "off") {
        parts.push(labels.followupStatusOffLabel);
    }
    else {
        if (followup.publications !== undefined &&
            followup.publications.length > 0) {
            parts.push(...followup.publications.map((publication) => renderFollowupPublicationSummary(publication, labels)));
        }
        if (followup.proposal !== undefined && followup.proposal !== null) {
            parts.push((0, catalog_1.translate)(locale, "canonicalWorkflowComment.followupProposalPendingLabel", {
                count: String(followup.proposal.items.length),
            }));
        }
        else if (followup.proposalPolicy === "always") {
            parts.push(labels.followupStatusRequestedLabel);
        }
        else if (followup.publications === undefined ||
            followup.publications.length === 0) {
            parts.push(labels.followupStatusAutoLabel);
        }
    }
    if (followup.recommendation !== undefined &&
        followup.recommendation !== null) {
        parts.push(followup.recommendation.recommended
            ? `${labels.followupRecommendedLabel}: ${followup.recommendation.reason}`
            : `${labels.followupNotRecommendedLabel}: ${followup.recommendation.reason}`);
    }
    const templateLabel = renderFollowupWorkflowSummary(workflowDisplayLabels, labels, locale);
    const renderedParts = parts.join(" / ");
    return followup.proposalPolicy === "off"
        ? renderedParts
        : `${renderedParts}${(0, catalog_1.translate)(locale, "canonicalWorkflowComment.followupTemplateSuffix", {
            template: templateLabel,
        })}`;
}
function renderFollowupConfigSummary(input) {
    return `${input.labels.followupProposalPolicyLabel}: \`${input.proposalPolicy}\` / ${input.labels.followupProposalApprovalLabel}: \`${input.proposalApproval}\``;
}
function renderFollowupPublicationSummary(publication, labels) {
    switch (publication.status) {
        case "created":
            return `${labels.followupCreatedLabel}: [${publication.issueDisplayId}](${publication.url})`;
        case "reused":
            return `${labels.followupReusedLabel}: [${publication.issueDisplayId}](${publication.url})`;
        case "failed":
            return `${labels.followupFailedLabel}: ${publication.reason}`;
        case "skipped":
            return `${labels.followupSkippedLabel}: ${publication.reason}`;
    }
}
function renderFollowupWorkflowSummary(workflowDisplayLabels, labels, locale) {
    const label = workflowDisplayLabels.workflowTemplateLabelsBySurface.followup ??
        (0, catalog_1.translate)(locale, "workflow.runContext.none");
    return label === "auto" ? labels.followupWorkflowAutoLabel : label;
}
function renderWorkflowStateLabel(workflowState, labels) {
    const schedulingLabel = renderSchedulingCurrentLabel(workflowState, labels);
    if (schedulingLabel !== null) {
        return schedulingLabel;
    }
    if ((0, tree_1.isWorkflowPlanningInProgress)(workflowState)) {
        return labels.currentStatusPlanningLabel;
    }
    switch ((0, tree_1.resolveWorkflowDisplayPhase)(workflowState)) {
        case "failed":
            return labels.currentStatusFailedLabel;
        case "done":
            return labels.currentStatusDoneLabel;
        case "pending":
            return labels.currentStatusPendingLabel;
        case "foreign_approval":
            return labels.currentStatusAssigneeApprovalLabel;
        case "start_approval":
            return labels.currentStatusPlanExecutionPendingLabel;
        default:
            return labels.currentStatusInProgressLabel;
    }
}
function renderSchedulingCurrentLabel(workflowState, labels) {
    const labelKey = (0, scheduling_1.resolveSchedulingCurrentLabelKey)(workflowState);
    return labelKey === null ? null : labels[labelKey];
}
function renderNextAction(workflowState, displayTree, labels, locale, displayTimeZone, issueId, currentStepResult) {
    const phase = (0, tree_1.resolveWorkflowDisplayPhase)(workflowState);
    if (phase === "foreign_approval") {
        return (0, approval_instructions_1.buildForeignApprovalNext)(locale, requireIssueIdForForeignApproval(issueId));
    }
    if (phase === "start_approval") {
        return (0, start_approval_1.renderStartApprovalNextAction)(locale, workflowState, issueId);
    }
    if (currentStepResult?.outcome === "fail") {
        return (0, catalog_1.translate)(locale, "run.presentation.failNext");
    }
    const actionRequired = (0, summary_1.selectActionRequiredLines)(workflowState);
    if (actionRequired !== null) {
        return labels.currentStatusNeedFromYouLabel;
    }
    const plan = workflowState.plan;
    if ((0, scheduling_1.isRecurringScheduleOnlyWorkflowState)(workflowState)) {
        return renderRecurringScheduleNextAction(workflowState, labels, locale, displayTimeZone);
    }
    if ((0, scheduling_1.isScheduledRunWorkflowState)(workflowState)) {
        return (0, scheduled_run_1.isCooldownScheduledRun)(workflowState)
            ? renderCooldownNextAction(workflowState, labels, locale, displayTimeZone)
            : renderScheduledRunNextAction(workflowState, labels, locale, displayTimeZone);
    }
    if (plan === null ||
        plan.steps.length === 0 ||
        displayTree.currentStepNode === null) {
        return (0, tree_1.isWorkflowPlanningInProgress)(workflowState)
            ? labels.currentStatusPlanningNextLabel
            : labels.currentStatusWorkflowPlanUnavailableLabel;
    }
    const progress = (0, tree_1.selectWorkflowPlanProgress)(plan);
    if (progress.isCompleted) {
        return labels.currentStatusWorkflowCompleteLabel;
    }
    return `${labels.currentStatusAiWillRunPrefix} ${displayTree.currentStepNode.pathLabel} ${(0, helpers_1.renderWorkflowStepTitleWithIssueRef)(displayTree.currentStepNode.step)}.`;
}
function requireIssueIdForForeignApproval(issueId) {
    if (issueId === null) {
        throw new Error("issueId is required for foreign approval instructions.");
    }
    return issueId;
}
function renderWorkflowTree(workflowState, displayTree, labels, locale, displayTimeZone) {
    const plan = workflowState.plan;
    if ((0, scheduling_1.isRecurringScheduleOnlyWorkflowState)(workflowState)) {
        return renderRecurringScheduleDetails(workflowState, labels, locale, displayTimeZone);
    }
    if ((0, scheduling_1.isScheduledRunWorkflowState)(workflowState)) {
        return (0, scheduled_run_1.isCooldownScheduledRun)(workflowState)
            ? renderCooldownDetails(workflowState, labels, locale, displayTimeZone)
            : renderScheduledRunDetails(workflowState, labels, locale, displayTimeZone);
    }
    if (plan === null || plan.steps.length === 0) {
        return (0, tree_1.isWorkflowPlanningInProgress)(workflowState)
            ? labels.currentStatusPlanningTreeUnavailableLabel
            : labels.currentStatusWorkflowPlanUnavailableLabel;
    }
    const context = {
        labels,
    };
    return displayTree.nodes
        .map((node) => renderDisplayNode(node, context))
        .join("\n\n");
}
function renderRecurringScheduleNextAction(workflowState, labels, locale, displayTimeZone) {
    return workflowState.nextRunAt
        ? `${labels.recurringNextRunLabel}: ${(0, display_time_1.formatHumanTimestamp)(workflowState.nextRunAt, locale, displayTimeZone)}.`
        : labels.recurringScheduleWaitingLabel;
}
function renderRecurringScheduleDetails(workflowState, labels, locale, displayTimeZone) {
    const schedule = workflowState.schedule;
    const lines = [
        `- **${labels.recurringScheduleLabel}:** ${renderScheduleState(schedule)}`,
        `- **${labels.recurringLastRunLabel}:** ${renderNullableTimestamp(workflowState.lastRunAt, locale, displayTimeZone)}`,
        `- **${labels.recurringNextRunLabel}:** ${renderNullableTimestamp(workflowState.nextRunAt, locale, displayTimeZone)}`,
        `- **${labels.recurringLastOutcomeLabel}:** ${renderNullableValue(workflowState.lastOutcome)}`,
    ];
    const autoStop = workflowState.recurring?.autoStop;
    if (autoStop !== undefined) {
        lines.push(`- **${labels.recurringAutoStopLabel}:**`);
        if (autoStop.stopAt !== null) {
            lines.push(`  - **${labels.recurringAutoStopAtLabel}:** ${renderNullableTimestamp(autoStop.stopAt, locale, displayTimeZone)}`);
        }
        if (autoStop.stopAfterRuns !== null) {
            lines.push(`  - **${labels.recurringAutoStopRunsLabel}:** ${autoStop.createdChildIssueCount}/${autoStop.stopAfterRuns}`);
        }
    }
    return lines.join("\n");
}
function renderScheduledRunNextAction(workflowState, labels, locale, displayTimeZone) {
    return workflowState.scheduledRun?.runAt
        ? `${labels.scheduledRunAtLabel}: ${(0, display_time_1.formatHumanTimestamp)(workflowState.scheduledRun.runAt, locale, displayTimeZone)}.`
        : labels.scheduledRunWaitingLabel;
}
function renderScheduledRunDetails(workflowState, labels, locale, displayTimeZone) {
    return [
        `- **${labels.scheduledRunAtLabel}:** ${renderNullableTimestamp(workflowState.scheduledRun?.runAt, locale, displayTimeZone)}`,
        `- **${labels.scheduledRunRequestedAtLabel}:** ${renderNullableTimestamp(workflowState.scheduledRun?.requestedAt, locale, displayTimeZone)}`,
    ].join("\n");
}
function renderCooldownNextAction(workflowState, labels, locale, displayTimeZone) {
    return workflowState.scheduledRun?.runAt
        ? `${labels.cooldownRetryAtLabel}: ${(0, display_time_1.formatHumanTimestamp)(workflowState.scheduledRun.runAt, locale, displayTimeZone)}.`
        : labels.cooldownWaitingLabel;
}
function renderCooldownDetails(workflowState, labels, locale, displayTimeZone) {
    const reason = (0, scheduled_run_1.resolveCooldownScheduledRunReason)(workflowState.scheduledRun);
    const reasonLabel = reason === "capacity"
        ? labels.cooldownReasonCapacity
        : labels.cooldownReasonQuotaExhausted;
    return [
        `- **${labels.cooldownReasonLabel}:** ${reasonLabel}`,
        `- **${labels.cooldownRetryAtLabel}:** ${renderNullableTimestamp(workflowState.scheduledRun?.runAt, locale, displayTimeZone)}`,
        `- **${labels.scheduledRunRequestedAtLabel}:** ${renderNullableTimestamp(workflowState.scheduledRun?.requestedAt, locale, displayTimeZone)}`,
    ].join("\n");
}
function renderScheduleState(schedule) {
    if (schedule === undefined || schedule.mode === "off") {
        return "off";
    }
    return `${schedule.mode} ${schedule.value}`;
}
function renderNullableValue(value) {
    return value ?? "-";
}
function renderNullableTimestamp(value, locale, displayTimeZone) {
    return value ? (0, display_time_1.formatHumanTimestamp)(value, locale, displayTimeZone) : "-";
}
function renderDisplayNode(node, context, depth = 0) {
    return node.kind === "step"
        ? renderStepNode(node, context, depth)
        : renderSubtreeNode(node, context, depth);
}
function renderStepNode(node, context, depth) {
    const nodePrefix = depth === 0 ? "" : `${"  ".repeat(depth - 1)}- `;
    const contentIndent = "  ".repeat(depth);
    const lines = [
        `${nodePrefix}**${node.pathLabel} ${(0, helpers_1.renderWorkflowStepTitleWithIssueRef)(node.step)} · ${renderNodeStatus(node.status, context.labels)}**`,
    ];
    lines.push("", ...renderStepSummaryLines(node.step, context, contentIndent));
    return lines.join("\n");
}
function renderStepSummaryLines(step, context, contentIndent) {
    const lines = [
        `${contentIndent}- **${context.labels.planObjectiveLabel}:** ${step.objective}`,
    ];
    const result = (0, results_1.coerceCanonicalWorkflowResult)(step.result);
    if (result !== null) {
        lines.push(`${contentIndent}- **${context.labels.finalResultSummaryLabel}:** ${result.resultSummary}`);
    }
    const detailLines = renderStepDetailsBlock(step, context.labels, contentIndent);
    if (detailLines.length > 0) {
        lines.push("", ...detailLines);
    }
    return lines;
}
function renderStepDetailsBlock(step, labels, contentIndent) {
    const instructionLines = renderWorkflowStepListItems(step.instructions, contentIndent);
    const doneWhenLines = renderWorkflowStepListItems(step.doneWhen, contentIndent);
    if (instructionLines.length === 0 && doneWhenLines.length === 0) {
        return [];
    }
    return renderDetailsBlock(labels.openStepDetailsSummary, [
        ...renderStepDetailSection(labels.stepInstructionsHeading, instructionLines, contentIndent),
        ...renderStepDetailSection(labels.stepDoneWhenHeading, doneWhenLines, contentIndent),
    ], contentIndent);
}
function renderStepDetailSection(heading, lines, contentIndent) {
    return lines.length === 0
        ? []
        : [`${contentIndent}#### ${heading}`, "", ...lines, ""];
}
function renderWorkflowStepListItems(items, contentIndent) {
    return items
        .map((item) => item.trim())
        .filter((item) => item.length > 0)
        .map((item) => `${contentIndent}- ${item}`);
}
function renderSubtreeNode(node, context, depth) {
    const nodePrefix = depth === 0 ? "" : `${"  ".repeat(depth - 1)}- `;
    const contentIndent = "  ".repeat(depth);
    const title = node.title ?? node.key.split(">").at(-1) ?? node.key;
    const lines = [
        `${nodePrefix}**${node.pathLabel} ${title} · ${renderNodeStatus(node.status, context.labels)}**`,
        ...(node.templateName === null
            ? []
            : [
                `${contentIndent}- **${context.labels.subtreeTemplateLabel}:** ${node.templateName}`,
            ]),
        `${contentIndent}- Progress: ${node.doneCount}/${node.totalCount}`,
        ...(node.replannedReason !== null
            ? [
                `${contentIndent}- ${context.labels.replannedLabel}: ${(0, helpers_1.renderValue)(node.replannedReason)}`,
            ]
            : []),
        "",
        ...renderDetailsBlock(context.labels.openSubtreeSummary, node.children.flatMap((child, index) => {
            const rendered = renderDisplayNode(child, context, depth + 1);
            return index === 0 ? [rendered] : ["", rendered];
        }), contentIndent),
    ];
    return lines.join("\n");
}
function renderDetailsBlock(summary, lines, indent, open = false) {
    return [
        `${indent}<details${open ? " open" : ""}>`,
        `${indent}<summary>${summary}</summary>`,
        "",
        ...lines,
        "",
        `${indent}</details>`,
    ];
}
function renderNodeStatus(status, labels) {
    switch (status) {
        case "Done":
            return labels.currentStatusDoneLabel;
        case "Pending":
            return labels.currentStatusPendingLabel;
        case "Failed":
            return labels.currentStatusFailedLabel;
        default:
            return labels.currentStatusInProgressLabel;
    }
}
function shouldRenderFinalResult(workflowState) {
    const progress = (0, tree_1.selectWorkflowPlanProgress)(workflowState.plan);
    return (workflowState.finalResult !== null &&
        workflowState.plan !== null &&
        progress.isCompleted);
}
function renderWorkflowResultSection(result, labels) {
    const canonical = (0, results_1.coerceCanonicalWorkflowFinalResult)(result);
    if (canonical === null) {
        return "";
    }
    const summaryLines = [
        `- ${labels.finalResultOutcomeLabel}: ${(0, results_1.renderWorkflowOutcomeValue)(canonical.outcome, labels)}`,
        `- ${labels.finalResultSummaryLabel}: ${(0, helpers_1.renderValue)(canonical.resultSummary)}`,
    ];
    return summaryLines.join("\n");
}
function renderFreeformListSection(lines) {
    return lines.map((line) => `- ${line}`).join("\n");
}
function normalizeNullableText(value) {
    if (value === undefined || value === null) {
        return null;
    }
    const trimmed = value.trim();
    return trimmed.length === 0 ? null : trimmed;
}
