"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateWorkflowPublishRequest = validateWorkflowPublishRequest;
exports.shouldDeferWorkflowStateCommit = shouldDeferWorkflowStateCommit;
exports.resolveTargetIssue = resolveTargetIssue;
exports.resolveExistingTargetIssueId = resolveExistingTargetIssueId;
exports.buildWorkflowTrackerFields = buildWorkflowTrackerFields;
exports.buildWorkflowProjectionChange = buildWorkflowProjectionChange;
exports.buildWorkflowPublishRequest = buildWorkflowPublishRequest;
exports.maybeSyncWorkflowProjection = maybeSyncWorkflowProjection;
exports.resolveTargetWorkflowProjection = resolveTargetWorkflowProjection;
exports.applyDependencyChanges = applyDependencyChanges;
exports.rollbackCreatedDependencies = rollbackCreatedDependencies;
exports.upsertWorkflowStateComment = upsertWorkflowStateComment;
exports.updateWorkflowStateCommentById = updateWorkflowStateCommentById;
exports.resolveWorkflowStateCommentFromComments = resolveWorkflowStateCommentFromComments;
exports.publishPreconditionMatches = publishPreconditionMatches;
exports.acknowledgeScannedActionableCommentsFromPolicy = acknowledgeScannedActionableCommentsFromPolicy;
const canonical_workflow_state_1 = require("../comments/canonical-workflow-state");
const registry_1 = require("../platform/ticket-tracker/registry");
const types_1 = require("../platform/ticket-tracker/types");
const workflow_labels_1 = require("../platform/ticket-tracker/workflow-labels");
const constants_1 = require("../worker/constants");
const publisher_events_1 = require("./publisher-events");
const publisher_projection_labels_1 = require("./publisher-projection-labels");
const publisher_shared_1 = require("./publisher-shared");
const workflow_timeline_scan_1 = require("./workflow-timeline-scan");
function validateWorkflowPublishRequest(request) {
    if (request.target.kind === "new_issue" &&
        (request.trackerFields?.title !== undefined ||
            request.trackerFields?.body !== undefined)) {
        throw new Error("new_issue target does not accept trackerFields.title/body.");
    }
    if (request.target.kind === "new_issue" &&
        request.newIssueFailurePolicy === undefined) {
        throw new Error("new_issue target requires newIssueFailurePolicy.");
    }
    if (request.target.kind !== "new_issue" &&
        request.newIssueFailurePolicy !== undefined) {
        throw new Error("newIssueFailurePolicy is only valid for new_issue target.");
    }
    if (request.newIssueFailurePolicy !== undefined &&
        request.newIssueFailurePolicy !== "leave_created_issue" &&
        request.newIssueFailurePolicy !== "close_created_internal_issue") {
        throw new Error("Invalid newIssueFailurePolicy.");
    }
    if (request.target.kind === "new_issue" &&
        request.terminalAction !== undefined) {
        throw new Error("terminalAction is not valid for new_issue target.");
    }
    if (request.publishPrecondition !== undefined) {
        validateWorkflowPublishPrecondition(request);
    }
    validateWorkflowCommentCursorPolicy(request.commentCursorPolicy);
    if (request.visibleUpdate?.kind === "workflow_update") {
        if (!(0, publisher_events_1.isWorkflowVisibleEvent)(request.visibleUpdate.event)) {
            throw new Error("Invalid workflow visible event.");
        }
        if (request.visibleUpdate.workerOutcome !== undefined &&
            !constants_1.WORKER_OUTCOME_VALUES.includes(request.visibleUpdate.workerOutcome)) {
            throw new Error("Invalid worker outcome.");
        }
        if (request.visibleUpdate.summary.trim().length === 0 ||
            request.visibleUpdate.next.trim().length === 0) {
            throw new Error("Workflow update summary and next are required.");
        }
        if (request.visibleUpdate.needsHumanReason !== undefined &&
            request.visibleUpdate.needsHumanReason !== null &&
            !(0, publisher_events_1.isWorkflowNeedsHumanReason)(request.visibleUpdate.needsHumanReason)) {
            throw new Error("Invalid workflow needs-human reason.");
        }
    }
    if (request.visibleUpdate?.kind === "feedback" &&
        request.visibleUpdate.summary.trim().length === 0) {
        throw new Error("Feedback summary is required.");
    }
    if (request.invalidWorkflowStatePolicy !== undefined &&
        request.invalidWorkflowStatePolicy !== "error" &&
        request.invalidWorkflowStatePolicy !== "treat_as_missing") {
        throw new Error("Invalid workflow state policy.");
    }
}
function shouldDeferWorkflowStateCommit(request) {
    return (request.target.kind !== "new_issue" &&
        request.workflowState !== null &&
        request.commentCursorPolicy.mode ===
            publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToPublishedCommentIfSafe);
}
function validateWorkflowPublishPrecondition(request) {
    if (request.target.kind === "new_issue") {
        throw new Error("publishPrecondition does not allow new_issue targets.");
    }
    const cursor = request.publishPrecondition?.expectedLastScannedCommentCursor ?? null;
    if (cursor !== null && !isWorkflowCommentCursorShape(cursor)) {
        throw new Error("Invalid publishPrecondition cursor.");
    }
}
function validateWorkflowCommentCursorPolicy(policy) {
    switch (policy.mode) {
        case publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve:
            return;
        case publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToScannedInput:
        case publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToPublishedCommentIfSafe:
            if (!Object.hasOwn(policy, "scannedInputCursor")) {
                throw new Error("scannedInputCursor is required for cursor advancement.");
            }
            if (policy.scannedInputCursor !== null &&
                !isWorkflowCommentCursorShape(policy.scannedInputCursor)) {
                throw new Error("Invalid scannedInputCursor.");
            }
            return;
        default:
            throw new Error("Invalid commentCursorPolicy mode.");
    }
}
function isWorkflowCommentCursorShape(cursor) {
    return (typeof cursor.commentId === "string" &&
        cursor.commentId.trim().length > 0 &&
        typeof cursor.createdAt === "string" &&
        cursor.createdAt.trim().length > 0);
}
async function resolveTargetIssue(publisher, request) {
    switch (request.target.kind) {
        case "new_issue":
            return publisher.ticketTracker.createIssue(buildCreateIssueInput(request.target, request.trackerFields));
        case "existing_issue": {
            const patch = buildPatchIssueInput(request.trackerFields);
            if (patch !== null) {
                return publisher.ticketTracker.patchIssueRecord(request.target.issueId, patch);
            }
            return (request.target.preloadedIssue ??
                publisher.ticketTracker.getIssueRecord(request.target.issueId));
        }
        case "pull_request_thread": {
            const patch = buildPatchIssueInput(request.trackerFields);
            if (patch !== null) {
                return publisher.ticketTracker.patchIssueRecord(request.target.pullRequestThreadId, patch);
            }
            return (request.target.preloadedIssue ??
                publisher.ticketTracker.getIssueRecord(request.target.pullRequestThreadId));
        }
    }
}
function resolveExistingTargetIssueId(target) {
    return target.kind === "pull_request_thread"
        ? target.pullRequestThreadId
        : target.issueId;
}
function buildWorkflowTrackerFields(input) {
    const trackerFields = {};
    if (input.title !== undefined) {
        trackerFields.title = input.title;
    }
    if (input.body !== undefined) {
        trackerFields.body = input.body;
    }
    if (input.assignees !== undefined) {
        trackerFields.assignees = [...input.assignees];
    }
    if (input.labelNames !== undefined) {
        trackerFields.labelNames = [...input.labelNames];
    }
    if (input.milestoneRef !== undefined) {
        trackerFields.milestoneRef = input.milestoneRef;
    }
    if (input.projectRef !== undefined) {
        trackerFields.projectRef = input.projectRef;
    }
    return hasWorkflowTrackerFields(trackerFields) ? trackerFields : undefined;
}
function buildWorkflowProjectionChange(input) {
    const projection = {
        previous: input.previous,
        next: input.next,
    };
    if (input.issueId !== undefined) {
        projection.issueId = input.issueId;
    }
    if (input.currentLabelNames !== undefined) {
        projection.currentLabelNames = [...input.currentLabelNames];
    }
    if (input.labelProjection !== undefined) {
        projection.labelProjection = input.labelProjection;
    }
    if (input.auxiliaryManagedLabels !== undefined) {
        projection.auxiliaryManagedLabels = input.auxiliaryManagedLabels;
    }
    return projection;
}
function buildWorkflowPublishRequest(input) {
    const request = {
        target: input.target,
        workflowState: input.workflowState,
        commentCursorPolicy: input.commentCursorPolicy ?? {
            mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve,
        },
        responseLanguage: input.responseLanguage,
    };
    if (input.visibleWorkflowState !== undefined) {
        request.visibleWorkflowState = input.visibleWorkflowState;
    }
    if (input.appendCanonicalWorkflowStateComment !== undefined) {
        request.appendCanonicalWorkflowStateComment =
            input.appendCanonicalWorkflowStateComment;
    }
    if (input.invalidWorkflowStatePolicy !== undefined) {
        request.invalidWorkflowStatePolicy = input.invalidWorkflowStatePolicy;
    }
    if (input.workflowProjection !== undefined) {
        request.workflowProjection = input.workflowProjection;
    }
    if (input.publishPrecondition !== undefined) {
        request.publishPrecondition = input.publishPrecondition;
    }
    if (hasWorkflowTrackerFields(input.trackerFields)) {
        request.trackerFields = input.trackerFields;
    }
    if (input.dependencyChanges !== undefined) {
        request.dependencyChanges = [...input.dependencyChanges];
    }
    if (input.visibleUpdate !== undefined) {
        request.visibleUpdate = input.visibleUpdate;
    }
    if (input.visibleUpdateDedupeCursor !== undefined) {
        request.visibleUpdateDedupeCursor = input.visibleUpdateDedupeCursor;
    }
    if (input.terminalAction !== undefined) {
        request.terminalAction = input.terminalAction;
    }
    if (input.newIssueFailurePolicy !== undefined) {
        request.newIssueFailurePolicy = input.newIssueFailurePolicy;
    }
    return request;
}
function buildCreateIssueInput(target, trackerFields) {
    const input = {
        title: target.title,
        body: target.body,
        assignees: dedupeTextValues(trackerFields?.assignees ?? []),
        labelNames: dedupeTextValues(trackerFields?.labelNames ?? []),
    };
    if (trackerFields?.milestoneRef !== undefined) {
        input.milestoneRef = trackerFields.milestoneRef;
    }
    if (trackerFields?.projectRef !== undefined) {
        input.projectRef = trackerFields.projectRef;
    }
    return input;
}
function buildPatchIssueInput(trackerFields) {
    if (trackerFields === undefined || Object.keys(trackerFields).length === 0) {
        return null;
    }
    const patch = {};
    if (trackerFields.title !== undefined) {
        patch.title = trackerFields.title;
    }
    if (trackerFields.body !== undefined) {
        patch.body = trackerFields.body;
    }
    if (trackerFields.assignees !== undefined) {
        patch.assignees = dedupeTextValues(trackerFields.assignees);
    }
    if (trackerFields.labelNames !== undefined) {
        patch.labelNames = dedupeTextValues(trackerFields.labelNames);
    }
    if (trackerFields.milestoneRef !== undefined) {
        patch.milestoneRef = trackerFields.milestoneRef;
    }
    if (trackerFields.projectRef !== undefined) {
        patch.projectRef = trackerFields.projectRef;
    }
    return patch;
}
async function maybeSyncWorkflowProjection(input) {
    const projection = input.projection;
    if (projection === undefined) {
        return false;
    }
    const issueId = projection.issueId ?? input.targetIssueId;
    const currentLabelNames = projection.currentLabelNames ??
        (await (0, publisher_projection_labels_1.resolveWorkflowProjectionCurrentLabelNames)({
            publisher: input.publisher,
            targetIssue: input.targetIssue,
            targetIssueLabelsAreFresh: input.targetIssueLabelsAreFresh,
            issueId,
        }));
    const syncInput = buildWorkflowProjectionSyncInput({
        targetIssueId: input.targetIssueId,
        projection,
        currentLabelNames,
    });
    if (isWorkflowProjectionNoop({
        workflowLabels: input.publisher.workflowLabels,
        syncInput,
    })) {
        return false;
    }
    await input.publisher.ticketTracker.syncWorkflowState(syncInput);
    return true;
}
function isWorkflowProjectionNoop(input) {
    void input.syncInput.previous;
    return (0, workflow_labels_1.labelsMatch)(input.syncInput.currentLabelNames ?? [], (0, workflow_labels_1.resolveWorkflowStateSyncLabelNames)(buildWorkflowStateSyncLabelNamesInput(input)));
}
function hasWorkflowTrackerFields(trackerFields) {
    return trackerFields !== undefined && Object.keys(trackerFields).length > 0;
}
function buildWorkflowProjectionSyncInput(input) {
    return {
        issueId: input.projection.issueId ?? input.targetIssueId,
        ...buildWorkflowProjectionChange({
            previous: input.projection.previous,
            next: input.projection.next,
            currentLabelNames: input.currentLabelNames,
            labelProjection: input.projection.labelProjection,
            auxiliaryManagedLabels: input.projection.auxiliaryManagedLabels,
        }),
    };
}
function buildWorkflowStateSyncLabelNamesInput(input) {
    const labelNamesInput = {
        workflowLabels: input.workflowLabels,
        currentLabelNames: input.syncInput.currentLabelNames ?? [],
        next: input.syncInput.next,
    };
    if (input.syncInput.labelProjection !== undefined) {
        Object.assign(labelNamesInput, {
            labelProjection: input.syncInput.labelProjection,
        });
    }
    if (input.syncInput.auxiliaryManagedLabels !== undefined) {
        Object.assign(labelNamesInput, {
            auxiliaryManagedLabels: input.syncInput.auxiliaryManagedLabels,
        });
    }
    return labelNamesInput;
}
function resolveTargetWorkflowProjection(input) {
    if (input.workflowProjection === undefined) {
        return (0, types_1.hasTrackerWorkflowState)(input.targetIssue)
            ? input.targetIssue.workflowState
            : null;
    }
    if (input.workflowProjection.issueId === undefined ||
        input.workflowProjection.issueId === input.targetIssue.id) {
        return input.workflowProjection.next;
    }
    return (0, types_1.hasTrackerWorkflowState)(input.targetIssue)
        ? input.targetIssue.workflowState
        : null;
}
async function applyDependencyChanges(input) {
    const applied = [];
    for (const change of input.changes) {
        const issueId = resolveIssueRef(change.issue, input.targetIssueId);
        const blockedByIssueId = resolveIssueRef(change.blockedBy, input.targetIssueId);
        const resolvedChange = {
            kind: change.kind,
            issue: { kind: "issue", issueId },
            blockedBy: { kind: "issue", issueId: blockedByIssueId },
        };
        const existingDependencies = await input.publisher.ticketTracker.listBlockingDependencies(issueId);
        const exists = existingDependencies.some((dependency) => dependency.id === blockedByIssueId);
        if (change.kind === "add_blocking_dependency") {
            if (exists) {
                continue;
            }
            await input.publisher.ticketTracker.addBlockingDependency(issueId, blockedByIssueId);
            applied.push(resolvedChange);
            input.createdDependencyChanges.push(resolvedChange);
            continue;
        }
        if (!exists) {
            continue;
        }
        await input.publisher.ticketTracker.removeBlockingDependency(issueId, blockedByIssueId);
        applied.push(resolvedChange);
    }
    return applied;
}
async function rollbackCreatedDependencies(input) {
    for (const change of [...input.changes].reverse()) {
        if (change.kind !== "add_blocking_dependency") {
            continue;
        }
        const issueId = change.issue.kind === "issue" ? change.issue.issueId : null;
        const blockedByIssueId = change.blockedBy.kind === "issue" ? change.blockedBy.issueId : null;
        if (issueId === null || blockedByIssueId === null) {
            continue;
        }
        await input.publisher.ticketTracker.removeBlockingDependency(issueId, blockedByIssueId);
    }
}
function resolveIssueRef(ref, targetIssueId) {
    return ref.kind === "target" ? targetIssueId : ref.issueId;
}
async function upsertWorkflowStateComment(input) {
    const resolution = input.append === true
        ? { kind: "missing" }
        : await resolveWorkflowStateComment(input);
    const body = await renderWorkflowStateCommentBody(input);
    if (resolution.kind === "found") {
        return {
            comment: await input.publisher.ticketTracker.updateComment(resolution.comment.id, body),
            updated: true,
        };
    }
    return {
        comment: await input.publisher.ticketTracker.createComment(input.issueId, body),
        updated: true,
    };
}
async function updateWorkflowStateCommentById(input) {
    const body = await renderWorkflowStateCommentBody(input);
    return input.publisher.ticketTracker.updateComment(input.commentId, body);
}
async function resolveWorkflowStateComment(input) {
    const comments = await input.publisher.ticketTracker.listComments(input.issueId, {
        timeline: "issue-only",
    });
    return resolveWorkflowStateCommentFromComments(comments, input.responseLanguage, input.invalidWorkflowStatePolicy);
}
function resolveWorkflowStateCommentFromComments(comments, responseLanguage, invalidWorkflowStatePolicy = "error") {
    const resolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)((0, workflow_timeline_scan_1.sortTimelineComments)([...comments]), responseLanguage);
    if (resolution.kind === "invalid") {
        if (invalidWorkflowStatePolicy === "treat_as_missing") {
            return { kind: "missing" };
        }
        throw new Error(resolution.summary);
    }
    return resolution;
}
function publishPreconditionMatches(input) {
    if (input.precondition.expectedCanonicalCommentId !== undefined &&
        (input.workflowStateComment?.id ?? null) !==
            input.precondition.expectedCanonicalCommentId) {
        return false;
    }
    if (input.precondition.expectedLastScannedCommentCursor !== undefined) {
        if ((input.workflowState?.lastScannedCommentCursor?.commentId ?? null) !==
            (input.precondition.expectedLastScannedCommentCursor?.commentId ??
                null) ||
            (input.workflowState?.lastScannedCommentCursor?.createdAt ?? null) !==
                (input.precondition.expectedLastScannedCommentCursor?.createdAt ?? null)) {
            return false;
        }
    }
    if (input.precondition.expectedPlanRevision !== undefined &&
        (input.workflowState?.planRevision ?? null) !==
            input.precondition.expectedPlanRevision) {
        return false;
    }
    if (input.precondition.expectedExecutionOwner === undefined) {
        return true;
    }
    if (input.workflowState === null) {
        return false;
    }
    return (input.workflowState.executionOwnership?.workerId ===
        input.precondition.expectedExecutionOwner.workerId &&
        input.workflowState.executionOwnership?.runId ===
            input.precondition.expectedExecutionOwner.runId);
}
async function renderWorkflowStateCommentBody(input) {
    return (0, canonical_workflow_state_1.formatCanonicalWorkflowStateCommentForRepo)(input.workflowState, input.publisher.repoRoot, {
        flavor: (0, registry_1.resolveTicketTrackerWorkflowStatePresentationFlavor)(input.publisher.ticketTrackerKind),
        locale: input.responseLanguage,
        displayTimeZone: input.publisher.displayTimeZone,
        lastUpdated: new Date().toISOString(),
        issueId: input.issueId,
    });
}
async function acknowledgeScannedActionableCommentsFromPolicy(input) {
    if (input.commentCursorPolicy.mode ===
        publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve) {
        await acknowledgeScannedActionableCommentsBetweenCursors({
            publisher: input.publisher,
            initialComments: input.initialComments,
            previousCursor: input.currentWorkflowState?.lastScannedCommentCursor ?? null,
            nextCursor: input.nextWorkflowState?.lastScannedCommentCursor ?? null,
        });
        return;
    }
    await acknowledgeScannedActionableCommentsBetweenCursors({
        publisher: input.publisher,
        initialComments: input.initialComments,
        previousCursor: input.currentWorkflowState?.lastScannedCommentCursor ?? null,
        nextCursor: input.commentCursorPolicy.scannedInputCursor,
    });
}
async function acknowledgeScannedActionableCommentsBetweenCursors(input) {
    if (cursorEquals(input.previousCursor, input.nextCursor)) {
        return;
    }
    if (typeof input.publisher.ticketTracker.acknowledgeComment !== "function") {
        return;
    }
    const actionableComments = (0, workflow_timeline_scan_1.selectTimelineWindowComments)({
        comments: input.initialComments,
        startCursor: input.previousCursor,
        throughCursor: input.nextCursor,
    }).filter((comment) => (0, workflow_timeline_scan_1.classifyWorkflowTimelineComment)(comment) === "actionable");
    if (actionableComments.length === 0) {
        return;
    }
    await Promise.allSettled(actionableComments.map((comment) => input.publisher.ticketTracker.acknowledgeComment(comment)));
}
function dedupeTextValues(values) {
    return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
}
function cursorEquals(left, right) {
    return (left?.commentId === right?.commentId && left?.createdAt === right?.createdAt);
}
