"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.publishVisibleUpdate = publishVisibleUpdate;
exports.resolvePrePublishCommentCursor = resolvePrePublishCommentCursor;
exports.resolveFinalCommentCursor = resolveFinalCommentCursor;
exports.maybeRefreshDeferredAiRunBudget = maybeRefreshDeferredAiRunBudget;
exports.maybeRefreshVisibleUpdateComment = maybeRefreshVisibleUpdateComment;
exports.applyScannedCommentCursor = applyScannedCommentCursor;
exports.cursorEquals = cursorEquals;
exports.attachWorkflowPublishCleanupErrors = attachWorkflowPublishCleanupErrors;
const managed_comments_1 = require("../comments/managed-comments");
const workflow_update_1 = require("../comments/workflow-update");
const display_duration_1 = require("../i18n/display-duration");
const publisher_events_1 = require("./publisher-events");
const publisher_shared_1 = require("./publisher-shared");
const publisher_state_support_1 = require("./publisher-state-support");
const template_bindings_1 = require("./template-bindings");
const workflow_timeline_scan_1 = require("./workflow-timeline-scan");
async function publishVisibleUpdate(input) {
    if (!shouldPublishVisibleUpdateComment(input.visibleUpdate)) {
        return {
            publishedComment: null,
            commentForRefresh: null,
        };
    }
    const { body, renderedUpdate } = renderVisibleUpdateBody({
        visibleUpdate: input.visibleUpdate,
        workflowState: input.workflowState,
        workflowStateComment: input.workflowStateComment,
        responseLanguage: input.responseLanguage,
    });
    if (input.visibleUpdate.dedupe !== false) {
        const latestVisibleUpdate = findLatestMatchingVisibleUpdate(input.initialComments, input.visibleUpdate, body, input.visibleUpdateDedupeCursor, input.commentCursorPolicy, renderedUpdate?.workflowTemplateLabel ?? null, renderedUpdate?.canonicalWorkflowStateUrl ?? null, renderedUpdate?.totalAiRunHours ?? null, renderedUpdate?.dedupeKey ?? null, input.responseLanguage);
        if (latestVisibleUpdate !== null) {
            return {
                publishedComment: null,
                commentForRefresh: latestVisibleUpdate,
            };
        }
    }
    const publishedComment = await input.publisher.ticketTracker.createComment(input.issueId, body);
    return {
        publishedComment,
        commentForRefresh: publishedComment,
    };
}
function shouldPublishVisibleUpdateComment(visibleUpdate) {
    if (visibleUpdate.kind !== "workflow_update") {
        return true;
    }
    if (visibleUpdate.workerOutcome === "wait_human" ||
        (visibleUpdate.needsHumanReason !== undefined &&
            visibleUpdate.needsHumanReason !== null) ||
        visibleUpdate.event === publisher_events_1.WORKFLOW_VISIBLE_EVENTS.humanAttentionRequested ||
        visibleUpdate.event === publisher_events_1.WORKFLOW_VISIBLE_EVENTS.issueReviewReady ||
        visibleUpdate.event === publisher_events_1.WORKFLOW_VISIBLE_EVENTS.prReviewReady ||
        visibleUpdate.event === publisher_events_1.WORKFLOW_VISIBLE_EVENTS.workflowFailed) {
        return true;
    }
    return publisher_events_1.WORKFLOW_VISIBLE_UPDATE_COMMENT_EVENT_ENABLED[visibleUpdate.event];
}
function buildWorkflowUpdateComment(input) {
    const workflowTemplateLabel = resolveWorkflowTemplateLabel({
        visibleUpdate: input.visibleUpdate,
        workflowState: input.workflowState,
    });
    const dedupeKey = normalizeOptionalWorkflowUpdateDedupeKey(input.visibleUpdate.dedupeKey);
    return {
        event: input.visibleUpdate.event,
        ...(input.visibleUpdate.title !== undefined
            ? { title: input.visibleUpdate.title }
            : {}),
        ...(dedupeKey !== null ? { dedupeKey } : {}),
        consecutiveAiRuns: input.workflowState?.consecutiveAiRuns ?? 0,
        ...(input.workflowState !== null
            ? { totalAiRunHours: input.workflowState.totalAiRunHours }
            : {}),
        summary: input.visibleUpdate.summary,
        next: input.visibleUpdate.next,
        ...(input.visibleUpdate.workerOutcome !== undefined
            ? { workerOutcome: input.visibleUpdate.workerOutcome }
            : {}),
        ...(input.visibleUpdate.needsHumanReason !== undefined &&
            input.visibleUpdate.needsHumanReason !== null
            ? { needsHumanReason: input.visibleUpdate.needsHumanReason }
            : {}),
        ...(workflowTemplateLabel !== null ? { workflowTemplateLabel } : {}),
        ...(input.workflowStateComment?.url !== undefined
            ? { canonicalWorkflowStateUrl: input.workflowStateComment.url }
            : {}),
    };
}
function resolveWorkflowTemplateLabel(input) {
    const explicitLabel = input.visibleUpdate.workflowTemplateLabel?.trim() ?? null;
    if (explicitLabel !== null && explicitLabel.length > 0) {
        return explicitLabel;
    }
    if (input.workflowState !== null) {
        return (0, template_bindings_1.getWorkflowTemplateName)((0, template_bindings_1.getActiveWorkflowTemplateBinding)(input.workflowState));
    }
    return null;
}
function formatFeedbackBody(visibleUpdate, responseLanguage) {
    return (0, managed_comments_1.formatManagedCommandFeedbackComment)([visibleUpdate.summary, visibleUpdate.next ?? null]
        .filter((part) => part !== null && part.trim().length > 0)
        .join("\n\n"), responseLanguage, visibleUpdate.headingKey);
}
function findLatestMatchingVisibleUpdate(comments, visibleUpdate, renderedBody, visibleUpdateDedupeCursor, commentCursorPolicy, renderedWorkflowTemplateLabel, renderedCanonicalWorkflowStateUrl, renderedTotalAiRunHours, renderedDedupeKey, responseLanguage) {
    if (visibleUpdate.kind === "feedback") {
        const matchingFeedback = [
            ...resolveVisibleUpdateDedupeComments(comments, visibleUpdateDedupeCursor, commentCursorPolicy),
        ]
            .reverse()
            .find((comment) => (0, managed_comments_1.isManagedCommandFeedbackComment)(comment.body) &&
            comment.body.trim() === renderedBody.trim());
        return matchingFeedback ?? null;
    }
    const candidateUpdates = [
        ...resolveVisibleUpdateDedupeComments(comments, visibleUpdateDedupeCursor, commentCursorPolicy),
    ].reverse();
    for (const comment of candidateUpdates) {
        const parsed = (0, workflow_update_1.parseWorkflowUpdate)(comment.body);
        if (parsed?.event !== visibleUpdate.event) {
            continue;
        }
        const parsedDedupeKey = parsed.dedupeKey ?? null;
        if (renderedDedupeKey !== null) {
            if (parsedDedupeKey === renderedDedupeKey) {
                return comment;
            }
            continue;
        }
        if (parsedDedupeKey !== null) {
            continue;
        }
        const totalAiRunHoursMatches = visibleUpdate.event === publisher_events_1.WORKFLOW_VISIBLE_EVENTS.workflowStarted ||
            (parsed.totalAiRunDuration ?? null) ===
                (renderedTotalAiRunHours === null
                    ? null
                    : (0, display_duration_1.formatHumanAiRunHours)(renderedTotalAiRunHours, responseLanguage));
        if (parsed.summary.trim() === visibleUpdate.summary.trim() &&
            parsed.next.trim() === visibleUpdate.next.trim() &&
            parsed.workerOutcome === visibleUpdate.workerOutcome &&
            (parsed.needsHumanReason ?? null) ===
                (visibleUpdate.needsHumanReason ?? null) &&
            totalAiRunHoursMatches &&
            (parsed.workflowTemplateLabel ?? null) ===
                renderedWorkflowTemplateLabel &&
            (parsed.canonicalWorkflowStateUrl ?? null) ===
                renderedCanonicalWorkflowStateUrl) {
            return comment;
        }
    }
    return null;
}
function normalizeOptionalWorkflowUpdateDedupeKey(dedupeKey) {
    if (dedupeKey === undefined || dedupeKey === null) {
        return null;
    }
    const normalized = dedupeKey.trim();
    return normalized.length > 0 ? normalized : null;
}
function resolveVisibleUpdateDedupeComments(comments, visibleUpdateDedupeCursor, commentCursorPolicy) {
    if (visibleUpdateDedupeCursor === null) {
        return (0, workflow_timeline_scan_1.sortTimelineComments)(comments).filter((comment) => (0, workflow_timeline_scan_1.classifyWorkflowTimelineComment)(comment) === "managed");
    }
    const dedupeAfterCursor = visibleUpdateDedupeCursor !== undefined
        ? visibleUpdateDedupeCursor
        : resolveVisibleUpdateDedupeAfterCursor(commentCursorPolicy);
    if (dedupeAfterCursor === undefined) {
        return comments;
    }
    const commentsAfterCursor = (0, workflow_timeline_scan_1.sliceCommentsAfterCursor)((0, workflow_timeline_scan_1.sortTimelineComments)(comments), dedupeAfterCursor);
    const dedupeComments = [];
    for (const comment of commentsAfterCursor) {
        if ((0, workflow_timeline_scan_1.classifyWorkflowTimelineComment)(comment) !== "managed") {
            break;
        }
        dedupeComments.push(comment);
    }
    return dedupeComments;
}
function resolveVisibleUpdateDedupeAfterCursor(policy) {
    if (policy.mode === publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve) {
        return undefined;
    }
    return policy.scannedInputCursor;
}
function resolvePrePublishCommentCursor(policy, workflowState) {
    if (policy.mode === publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve) {
        return {
            known: true,
            cursor: workflowState?.lastScannedCommentCursor ?? null,
        };
    }
    if (policy.mode === publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToScannedInput) {
        return { known: true, cursor: policy.scannedInputCursor };
    }
    return { known: false, cursor: null };
}
async function resolveFinalCommentCursor(input) {
    const policy = input.policy;
    if (policy.mode === publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve) {
        return null;
    }
    if (policy.mode ===
        publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToScannedInput ||
        input.visibleUpdateComment === null) {
        return policy.scannedInputCursor;
    }
    const comments = (0, workflow_timeline_scan_1.sortTimelineComments)(await input.publisher.ticketTracker.listComments(input.issueId, {
        timeline: "issue-only",
    }));
    const publishedIndex = comments.findIndex((comment) => comment.id === input.visibleUpdateComment?.id);
    if (publishedIndex === -1) {
        return policy.scannedInputCursor;
    }
    const safeIntermediaryCommentIds = new Set();
    if (input.workflowStateComment !== null) {
        safeIntermediaryCommentIds.add(input.workflowStateComment.id);
    }
    if (policy.scannedInputCursor === null) {
        return comments
            .slice(0, publishedIndex)
            .every((comment) => safeIntermediaryCommentIds.has(comment.id))
            ? (0, workflow_timeline_scan_1.toWorkflowCommentCursor)(input.visibleUpdateComment)
            : null;
    }
    const commentsAfterScan = (0, workflow_timeline_scan_1.sliceCommentsAfterCursor)(comments, policy.scannedInputCursor);
    const publishedAfterScanIndex = commentsAfterScan.findIndex((comment) => comment.id === input.visibleUpdateComment?.id);
    if (publishedAfterScanIndex === -1) {
        return policy.scannedInputCursor;
    }
    return commentsAfterScan
        .slice(0, publishedAfterScanIndex)
        .every((comment) => safeIntermediaryCommentIds.has(comment.id))
        ? (0, workflow_timeline_scan_1.toWorkflowCommentCursor)(input.visibleUpdateComment)
        : policy.scannedInputCursor;
}
function maybeRefreshDeferredAiRunBudget(input) {
    const initialConsecutiveAiRuns = resolveWorkflowConsecutiveAiRunsFromComments({
        comments: input.initialComments,
        responseLanguage: input.responseLanguage,
    });
    if (initialConsecutiveAiRuns === null ||
        input.workflowState.consecutiveAiRuns !== initialConsecutiveAiRuns + 1) {
        return input.workflowState;
    }
    const currentConsecutiveAiRuns = resolveWorkflowConsecutiveAiRunsFromComments({
        comments: input.currentComments,
        responseLanguage: input.responseLanguage,
    });
    if (currentConsecutiveAiRuns === null ||
        currentConsecutiveAiRuns + 1 === input.workflowState.consecutiveAiRuns) {
        return input.workflowState;
    }
    return {
        ...input.workflowState,
        consecutiveAiRuns: currentConsecutiveAiRuns + 1,
        totalAiRunHours: resolveWorkflowTotalAiRunHoursFromComments({
            comments: input.currentComments,
            responseLanguage: input.responseLanguage,
        }) ?? input.workflowState.totalAiRunHours,
    };
}
async function maybeRefreshVisibleUpdateComment(input) {
    if (input.visibleUpdate.kind !== "workflow_update" ||
        input.visibleUpdateComment === null) {
        return input.visibleUpdateComment;
    }
    const { body } = renderVisibleUpdateBody({
        visibleUpdate: input.visibleUpdate,
        workflowState: input.workflowState,
        workflowStateComment: input.workflowStateComment,
        responseLanguage: input.responseLanguage,
    });
    if (body.trim() === input.visibleUpdateComment.body.trim()) {
        return input.visibleUpdateComment;
    }
    return input.publisher.ticketTracker.updateComment(input.visibleUpdateComment.id, body);
}
function renderVisibleUpdateBody(input) {
    const renderedUpdate = input.visibleUpdate.kind === "workflow_update"
        ? buildWorkflowUpdateComment({
            visibleUpdate: input.visibleUpdate,
            workflowState: input.workflowState,
            workflowStateComment: input.workflowStateComment,
        })
        : null;
    if (input.visibleUpdate.kind === "feedback") {
        return {
            body: formatFeedbackBody(input.visibleUpdate, input.responseLanguage),
            renderedUpdate: null,
        };
    }
    if (renderedUpdate === null) {
        throw new Error("Missing workflow update render input.");
    }
    return {
        body: (0, workflow_update_1.formatWorkflowUpdate)(renderedUpdate, {
            language: input.responseLanguage,
        }),
        renderedUpdate,
    };
}
function resolveWorkflowConsecutiveAiRunsFromComments(input) {
    const resolution = (0, publisher_state_support_1.resolveWorkflowStateCommentFromComments)(input.comments, input.responseLanguage);
    if (resolution.kind !== "found") {
        return null;
    }
    return (0, workflow_timeline_scan_1.hasActionableWorkflowCommentAfterCursor)(input.comments, resolution.workflowState.lastScannedCommentCursor ?? null)
        ? 0
        : resolution.workflowState.consecutiveAiRuns;
}
function resolveWorkflowTotalAiRunHoursFromComments(input) {
    const resolution = (0, publisher_state_support_1.resolveWorkflowStateCommentFromComments)(input.comments, input.responseLanguage);
    if (resolution.kind !== "found") {
        return null;
    }
    return (0, workflow_timeline_scan_1.hasActionableWorkflowCommentAfterCursor)(input.comments, resolution.workflowState.lastScannedCommentCursor ?? null)
        ? 0
        : resolution.workflowState.totalAiRunHours;
}
function applyScannedCommentCursor(workflowState, cursor) {
    return {
        ...workflowState,
        lastScannedCommentCursor: cursor,
    };
}
function cursorEquals(left, right) {
    return (left?.commentId === right?.commentId && left?.createdAt === right?.createdAt);
}
function attachWorkflowPublishCleanupErrors(error, cleanupErrors) {
    if (!(error instanceof Error) || cleanupErrors.length === 0) {
        return;
    }
    const nextCause = cleanupErrors.length === 1
        ? cleanupErrors[0]
        : new AggregateError(cleanupErrors, "Workflow publish cleanup failed.");
    const currentCause = error.cause;
    const mergedCause = currentCause === undefined
        ? nextCause
        : new AggregateError([currentCause, nextCause], "Workflow publish failed with supplemental cleanup errors.");
    Object.defineProperty(error, "cause", {
        value: mergedCause,
        configurable: true,
        enumerable: false,
        writable: true,
    });
}
