"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveForeignApprovalRequirement = resolveForeignApprovalRequirement;
exports.resolvePreCanonicalForeignInputApproval = resolvePreCanonicalForeignInputApproval;
const node_crypto_1 = require("node:crypto");
const workflow_timeline_scan_1 = require("./workflow-timeline-scan");
async function resolveForeignApprovalRequirement(input) {
    const result = await evaluateForeignApprovalTimeline({
        ticketTracker: input.ticketTracker,
        config: input.config,
        ...(input.issue !== undefined ? { issue: input.issue } : {}),
        comments: input.comments,
        ...(input.linkedIssue !== undefined
            ? { linkedIssue: input.linkedIssue }
            : {}),
        ...(input.linkedIssueComments !== undefined
            ? { linkedIssueComments: input.linkedIssueComments }
            : {}),
        scanStartCursor: input.scanStartCursor,
        currentAssignee: input.currentAssignee,
        ...(input.approvalState !== undefined
            ? { approvalState: input.approvalState }
            : {}),
        ...(input.throughCursor !== undefined
            ? { throughCursor: input.throughCursor }
            : {}),
        ...(input.includeThroughCursor !== undefined
            ? { includeThroughCursor: input.includeThroughCursor }
            : {}),
    });
    return result.required && result.digest !== null
        ? {
            required: true,
            triggerComment: result.triggerComment,
            digest: result.digest,
        }
        : { required: false };
}
async function resolvePreCanonicalForeignInputApproval(input) {
    if (input.approvedAssignee === null) {
        return undefined;
    }
    let latestApproveComment = null;
    let latestForeignActionableComment = null;
    for (const comment of input.comments) {
        switch ((0, workflow_timeline_scan_1.classifyWorkflowTimelineComment)(comment)) {
            case "managed":
            case "workflow_command":
            case "start_command":
                break;
            case "approve_command":
                if (comment.author === input.approvedAssignee) {
                    latestApproveComment = comment;
                }
                break;
            case "actionable":
                if (comment.author !== input.approvedAssignee) {
                    latestForeignActionableComment = comment;
                }
                break;
        }
    }
    if (latestApproveComment === null ||
        (latestForeignActionableComment !== null &&
            latestForeignActionableComment.createdAt > latestApproveComment.createdAt)) {
        return undefined;
    }
    const snapshot = input.ticketTracker !== undefined && input.config !== undefined
        ? await resolveForeignInputSnapshot({
            ticketTracker: input.ticketTracker,
            config: input.config,
            issue: input.issue ?? null,
            comments: input.comments,
            linkedIssue: input.linkedIssue ?? null,
            linkedIssueComments: input.linkedIssueComments ?? [],
            currentAssignee: input.approvedAssignee,
        })
        : resolveCommentOnlyForeignInputSnapshot({
            comments: input.comments,
            approvedAssignee: input.approvedAssignee,
        });
    if (snapshot.digest === null) {
        return undefined;
    }
    return {
        approvedBy: input.approvedAssignee,
        approvedAssignee: input.approvedAssignee,
        approvedAt: latestApproveComment.createdAt,
        approvedDigest: snapshot.digest,
        approvedThroughCursor: (0, workflow_timeline_scan_1.toWorkflowCommentCursor)(latestApproveComment),
    };
}
async function evaluateForeignApprovalTimeline(input) {
    const persistedApproval = input.approvalState !== undefined &&
        input.approvalState.approvedAssignee === input.currentAssignee
        ? input.approvalState
        : undefined;
    const snapshot = await resolveForeignInputSnapshot({
        ticketTracker: input.ticketTracker,
        config: input.config,
        issue: input.issue ?? null,
        comments: input.comments,
        linkedIssue: input.linkedIssue ?? null,
        linkedIssueComments: input.linkedIssueComments ?? [],
        currentAssignee: input.currentAssignee,
        ...(input.throughCursor !== undefined
            ? { throughCursor: input.throughCursor }
            : {}),
        ...(input.includeThroughCursor !== undefined
            ? { includeThroughCursor: input.includeThroughCursor }
            : {}),
    });
    const approvalMatchesDigest = persistedApproval !== undefined &&
        snapshot.digest !== null &&
        persistedApproval.approvedDigest !== undefined &&
        persistedApproval.approvedDigest === snapshot.digest;
    const required = snapshot.digest !== null && !approvalMatchesDigest;
    return {
        approvalState: persistedApproval,
        digest: snapshot.digest,
        triggerComment: snapshot.triggerComment,
        required,
    };
}
function createApprovalInvalidationEvaluator(input) {
    const membershipByLogin = new Map();
    let repositoryOwnerKind = null;
    return async (comment) => {
        if (input.currentAssignee !== null &&
            comment.author === input.currentAssignee) {
            return false;
        }
        if (input.config.foreignIssueApprovalScope === "all_foreign") {
            return true;
        }
        repositoryOwnerKind ??= input.ticketTracker.getRepositoryOwnerKind();
        if ((await repositoryOwnerKind) !== "organization") {
            return false;
        }
        let membership = membershipByLogin.get(comment.author);
        if (membership === undefined) {
            membership = input.ticketTracker
                .getOrganizationMembership(comment.author)
                .then((result) => {
                if (result === "unknown") {
                    throw new Error(`Could not determine project membership for ${comment.author}.`);
                }
                return result === "non_member";
            });
            membershipByLogin.set(comment.author, membership);
        }
        return membership;
    };
}
async function resolveForeignInputSnapshot(input) {
    const invalidatesApproval = createApprovalInvalidationEvaluator({
        ticketTracker: input.ticketTracker,
        config: input.config,
        currentAssignee: input.currentAssignee,
    });
    const sources = [];
    let triggerComment = null;
    const issueComments = input.throughCursor !== undefined
        ? (0, workflow_timeline_scan_1.selectTimelineWindowComments)({
            comments: input.comments,
            startCursor: null,
            throughCursor: input.throughCursor,
            ...(input.includeThroughCursor !== undefined
                ? { includeThroughCursor: input.includeThroughCursor }
                : {}),
        })
        : input.comments;
    for (const comment of [...issueComments, ...input.linkedIssueComments]) {
        if ((0, workflow_timeline_scan_1.classifyWorkflowTimelineComment)(comment) !== "actionable") {
            continue;
        }
        if (!(await invalidatesApproval(comment))) {
            continue;
        }
        triggerComment = comment;
        sources.push([
            "comment",
            comment.targetDisplayId,
            comment.id,
            comment.author,
            comment.updatedAt ?? comment.createdAt,
            comment.body,
        ].join("\u241f"));
    }
    const issueBodies = [
        input.issue === null
            ? null
            : await resolveForeignIssueBodySource({
                ticketTracker: input.ticketTracker,
                config: input.config,
                issue: input.issue,
                currentAssignee: input.currentAssignee,
            }),
        input.linkedIssue === null
            ? null
            : await resolveForeignIssueBodySource({
                ticketTracker: input.ticketTracker,
                config: input.config,
                issue: input.linkedIssue,
                currentAssignee: input.currentAssignee,
            }),
    ].filter((value) => value !== null);
    if (sources.length === 0 && issueBodies.length === 0) {
        return { digest: null, triggerComment };
    }
    const digest = (0, node_crypto_1.createHash)("sha256")
        .update([...issueBodies, ...sources].sort().join("\u241e"), "utf8")
        .digest("hex");
    return { digest, triggerComment };
}
async function resolveForeignIssueBodySource(input) {
    const body = input.issue.body.trim();
    if (body.length === 0 || input.issue.author === null) {
        return null;
    }
    if (input.currentAssignee !== null &&
        input.issue.author === input.currentAssignee) {
        return null;
    }
    const repositoryOwnerKind = await input.ticketTracker.getRepositoryOwnerKind();
    if (input.config.foreignIssueApprovalScope === "external_only") {
        if (repositoryOwnerKind !== "organization") {
            return null;
        }
        const membership = await input.ticketTracker.getOrganizationMembership(input.issue.author);
        if (membership !== "non_member") {
            if (membership === "unknown") {
                throw new Error(`Could not determine project membership for ${input.issue.author}.`);
            }
            return null;
        }
    }
    return ["issue_body", input.issue.id, input.issue.author, body].join("\u241f");
}
function resolveCommentOnlyForeignInputSnapshot(input) {
    const sources = [];
    let triggerComment = null;
    for (const comment of input.comments) {
        if ((0, workflow_timeline_scan_1.classifyWorkflowTimelineComment)(comment) !== "actionable") {
            continue;
        }
        if (comment.author === input.approvedAssignee) {
            continue;
        }
        triggerComment = comment;
        sources.push([
            "comment",
            comment.targetDisplayId,
            comment.id,
            comment.author,
            comment.updatedAt ?? comment.createdAt,
            comment.body,
        ].join("\u241f"));
    }
    if (sources.length === 0) {
        return { digest: null, triggerComment };
    }
    return {
        digest: (0, node_crypto_1.createHash)("sha256")
            .update(sources.sort().join("\u241e"), "utf8")
            .digest("hex"),
        triggerComment,
    };
}
