"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.scanWorkflowTimeline = scanWorkflowTimeline;
exports.isActionableWorkflowComment = isActionableWorkflowComment;
exports.findLatestActionableWorkflowComment = findLatestActionableWorkflowComment;
exports.findActionableWorkflowCommentsAfterCursor = findActionableWorkflowCommentsAfterCursor;
exports.hasActionableWorkflowCommentAfterCursor = hasActionableWorkflowCommentAfterCursor;
exports.toWorkflowCommentCursor = toWorkflowCommentCursor;
exports.classifyWorkflowTimelineComment = classifyWorkflowTimelineComment;
exports.sliceCommentsAfterCursor = sliceCommentsAfterCursor;
exports.sliceCommentsThroughCursor = sliceCommentsThroughCursor;
exports.selectTimelineWindowComments = selectTimelineWindowComments;
exports.sortTimelineComments = sortTimelineComments;
const managed_comments_1 = require("../comments/managed-comments");
const comment_command_1 = require("./comment-command");
const workflow_option_normalizer_1 = require("./workflow-option-normalizer");
function scanWorkflowTimeline(input) {
    const scanStartCursor = input.workflowState?.lastScannedCommentCursor ?? null;
    const commentsAfterCursor = selectTimelineWindowComments({
        comments: input.comments,
        startCursor: scanStartCursor,
    });
    const commandEvents = [];
    const workflowCommandEvents = [];
    const operationalCommandEvents = [];
    const actionableCommentsAfterCursor = [];
    for (const comment of commentsAfterCursor) {
        const commentClass = classifyWorkflowTimelineComment(comment);
        switch (commentClass) {
            case "workflow_command": {
                const command = (0, comment_command_1.parseWorkflowCommandComment)(comment.body);
                if (command.kind === "none") {
                    break;
                }
                const event = {
                    kind: "workflow",
                    source: "comment",
                    comment,
                    command,
                };
                commandEvents.push(event);
                workflowCommandEvents.push(event);
                break;
            }
            case "approve_command": {
                const event = {
                    kind: "approve",
                    comment,
                };
                commandEvents.push(event);
                operationalCommandEvents.push(event);
                break;
            }
            case "start_command": {
                const event = {
                    kind: "start",
                    comment,
                };
                commandEvents.push(event);
                operationalCommandEvents.push(event);
                break;
            }
            case "actionable":
                actionableCommentsAfterCursor.push(comment);
                break;
            case "managed":
                break;
        }
    }
    if (input.workflowState === null && workflowCommandEvents.length === 0) {
        const bodyCommand = resolveWorkflowCommandFromIssueBody(input.issueBody);
        if (bodyCommand !== null) {
            commandEvents.unshift(bodyCommand);
            workflowCommandEvents.unshift(bodyCommand);
        }
    }
    return {
        scannedInputCursor: resolveScannedInputCursor(commentsAfterCursor, scanStartCursor),
        scanStartCursor,
        commentsAfterCursor,
        commandEvents,
        workflowCommandEvents,
        operationalCommandEvents,
        latestActionableComment: actionableCommentsAfterCursor[actionableCommentsAfterCursor.length - 1] ??
            null,
        actionableCommentsAfterCursor,
    };
}
function isActionableWorkflowComment(comment) {
    return classifyWorkflowTimelineComment(comment) === "actionable";
}
function findLatestActionableWorkflowComment(comments) {
    for (const comment of [...sortTimelineComments(comments)].reverse()) {
        if (isActionableWorkflowComment(comment)) {
            return comment;
        }
    }
    return null;
}
function findActionableWorkflowCommentsAfterCursor(comments, cursor) {
    return selectTimelineWindowComments({
        comments,
        startCursor: cursor,
    }).filter(isActionableWorkflowComment);
}
function hasActionableWorkflowCommentAfterCursor(comments, cursor) {
    return findActionableWorkflowCommentsAfterCursor(comments, cursor).length > 0;
}
function resolveScannedInputCursor(commentsAfterCursor, scanStartCursor) {
    const comment = commentsAfterCursor[commentsAfterCursor.length - 1];
    if (comment !== undefined) {
        return toWorkflowCommentCursor(comment);
    }
    return scanStartCursor;
}
function toWorkflowCommentCursor(comment) {
    return {
        commentId: comment.id,
        createdAt: comment.createdAt,
    };
}
function classifyWorkflowTimelineComment(comment) {
    if ((0, managed_comments_1.isManagedComment)(comment)) {
        return "managed";
    }
    if ((0, comment_command_1.parseApproveCommandComment)(comment.body)) {
        return "approve_command";
    }
    if ((0, comment_command_1.parseStartCommandComment)(comment.body)) {
        return "start_command";
    }
    const lines = comment.body.split(/\r?\n/);
    const firstNonEmptyLine = lines.find((line) => line.trim().length > 0);
    if (firstNonEmptyLine !== undefined &&
        (0, comment_command_1.parseWorkflowCommandFirstLine)(firstNonEmptyLine).kind === "workflow") {
        return "workflow_command";
    }
    return "actionable";
}
function sliceCommentsAfterCursor(comments, cursor) {
    if (cursor === null) {
        return [...comments];
    }
    const cursorIndex = comments.findIndex((comment) => isMatchingTimelineCursorComment(comment, cursor));
    if (cursorIndex !== -1) {
        return comments.slice(cursorIndex + 1);
    }
    return comments.filter((comment) => {
        const createdComparison = compareTimelineTimestamp(comment.createdAt, cursor.createdAt);
        return (createdComparison > 0 ||
            (createdComparison === 0 && comment.id !== cursor.commentId));
    });
}
function sliceCommentsThroughCursor(comments, cursor, options) {
    if (cursor === null) {
        return [];
    }
    const includeCursor = options?.includeCursor !== false;
    const cursorIndex = comments.findIndex((comment) => isMatchingTimelineCursorComment(comment, cursor));
    if (cursorIndex !== -1) {
        return comments.slice(0, includeCursor ? cursorIndex + 1 : cursorIndex);
    }
    return comments.filter((comment) => {
        const createdComparison = compareTimelineTimestamp(comment.createdAt, cursor.createdAt);
        return includeCursor
            ? createdComparison < 0 ||
                (createdComparison === 0 && comment.id !== cursor.commentId)
            : createdComparison < 0;
    });
}
function selectTimelineWindowComments(input) {
    const comments = sortTimelineComments(input.comments);
    const commentsAfterStart = sliceCommentsAfterCursor(comments, input.startCursor);
    if (input.throughCursor === undefined) {
        return commentsAfterStart;
    }
    return sliceCommentsThroughCursor(commentsAfterStart, input.throughCursor, {
        includeCursor: input.includeThroughCursor !== false,
    });
}
function sortTimelineComments(comments) {
    return comments
        .map((comment, index) => ({ comment, index }))
        .sort((left, right) => {
        const createdComparison = compareTimelineTimestamp(left.comment.createdAt, right.comment.createdAt);
        return createdComparison === 0
            ? left.index - right.index
            : createdComparison;
    })
        .map(({ comment }) => comment);
}
function isMatchingTimelineCursorComment(comment, cursor) {
    return (comment.id === cursor.commentId &&
        compareTimelineTimestamp(comment.createdAt, cursor.createdAt) === 0);
}
function compareTimelineTimestamp(left, right) {
    const leftMs = Date.parse(left);
    const rightMs = Date.parse(right);
    if (Number.isFinite(leftMs) && Number.isFinite(rightMs)) {
        return leftMs - rightMs;
    }
    return left.localeCompare(right);
}
function resolveWorkflowCommandFromIssueBody(issueBody) {
    if (!issueBody) {
        return null;
    }
    const firstLine = issueBody
        .split(/\r?\n/)
        .find((line) => line.trim().length > 0);
    if (firstLine === undefined ||
        (0, comment_command_1.parseWorkflowCommandFirstLine)(firstLine).kind !== "workflow") {
        return null;
    }
    const firstLineCommand = (0, comment_command_1.parseWorkflowCommandFirstLine)(firstLine);
    if (firstLineCommand.kind !== "workflow") {
        return null;
    }
    const command = (0, workflow_option_normalizer_1.parseWorkflowOptionTail)(firstLineCommand.rawCommandTail);
    return {
        kind: "workflow",
        source: "body",
        command,
    };
}
