"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadIssueShowViewModel = loadIssueShowViewModel;
const canonical_workflow_state_1 = require("../comments/canonical-workflow-state");
const workflow_update_1 = require("../comments/workflow-update");
const catalog_1 = require("../i18n/catalog");
const issue_report_1 = require("../issues/issue-report");
const workflow_labels_1 = require("../platform/ticket-tracker/workflow-labels");
const child_issue_title_1 = require("../run/run-once-cycle/child-issue-title");
const child_workflow_state_1 = require("../run/run-once-cycle/child-workflow-state");
const recurring_child_dependencies_1 = require("../run/run-once-cycle/recurring-child-dependencies");
const shared_1 = require("../run/run-once-cycle/shared");
const display_labels_1 = require("../workflows/display-labels");
const recurring_1 = require("../workflows/recurring");
const issue_ref_1 = require("../workflows/issue-ref");
const issue_related_pull_request_1 = require("./issue-related-pull-request");
const issue_show_options_1 = require("./issue-show-options");
const issue_show_comments_1 = require("./issue-show-comments");
const issue_show_pr_details_1 = require("./issue-show-pr-details");
const issue_rendering_1 = require("./issue-rendering");
const post_completion_comments_1 = require("../issues/post-completion-comments");
async function loadIssueShowViewModel(clients, inputOptions) {
    const { options, issue, workflowState, comments, postCompletionCommentCandidates, pr, blockingDependencies, prDetails, latestUpdate, canonicalResolution, canonicalWorkflowState, foundWorkflowState, } = await loadIssueShowSourceData(clients, inputOptions);
    const originIssues = await loadIssueParentChainViewModel({
        ticketTracker: clients.ticketTracker,
        currentIssue: issue,
        workflowState: foundWorkflowState,
        locale: clients.locale,
        displayTimeZone: clients.config.i18n.displayTimeZone,
        depth: options.parentsDepth,
    });
    const postCompletionComments = buildPostCompletionCommentsViewModel({
        issue,
        pr,
        candidates: postCompletionCommentCandidates,
        mode: options.commentsMode,
    });
    const displayedComments = resolveDisplayedComments({
        comments,
        mode: options.commentsMode,
    });
    const childIssues = options.childrenDepth === 0
        ? { requested: false, groups: [], hiddenCount: 0 }
        : await loadIssueShowChildIssuesViewModel({
            clients,
            options,
            parentIssue: issue,
            workflowState: foundWorkflowState,
            blockingDependencies,
            remainingDepth: options.childrenDepth,
            currentDepth: 1,
            visitedIssueIds: new Set([issue.id]),
            relatedLimitBudget: options.relatedLimit === null
                ? null
                : { remaining: options.relatedLimit },
        });
    const completedAt = resolveCompletedAt(issue, pr);
    return {
        isManaged: workflowState !== null,
        issue: {
            displayId: issue.displayId,
            title: issue.title,
            body: issue.body,
            url: issue.url,
            isOpen: issue.isOpen,
            workflowState: workflowState === null ? null : { status: workflowState.status },
        },
        pr,
        prDetails,
        blockingLabel: (0, issue_rendering_1.formatOpenDependencyLabel)(blockingDependencies),
        comments: displayedComments,
        postCompletionComments,
        latestUpdate,
        latestUpdateHeadingLabel: resolveLatestUpdateHeadingLabel(latestUpdate, canonicalResolution, clients.locale),
        canonicalWorkflowState,
        ...(0, display_labels_1.resolveWorkflowDisplayLabels)({
            catalog: clients.workflowDisplayCatalog,
            activeSurface: canonicalWorkflowState?.workflowSurface,
            workflowTemplates: canonicalWorkflowState?.workflowTemplates,
        }),
        followupProposalPolicy: canonicalWorkflowState?.followupProposalPolicy ?? "off",
        followupProposalApproval: canonicalWorkflowState?.followupProposalApproval ?? "auto",
        workflowStatusLabel: workflowState === null
            ? (0, catalog_1.translate)(clients.locale, "issue.command.show.none")
            : (0, workflow_labels_1.getWorkflowLabelForStatus)(clients.workflowLabels, workflowState.status),
        ruleLabelsLabel: workflowState === null
            ? (0, catalog_1.translate)(clients.locale, "issue.command.show.none")
            : (0, issue_rendering_1.buildRuleLabelsLabel)(workflowState.rules, clients.workflowLabels),
        assigneesLabel: issue.assignees.length > 0 ? issue.assignees.join(", ") : "-",
        updatedAtLabel: (0, issue_report_1.formatIssueUpdatedAt)(issue.updatedAt, clients.locale, clients.config.i18n.displayTimeZone),
        completedAtLabel: completedAt === null
            ? null
            : (0, issue_report_1.formatIssueUpdatedAt)(completedAt, clients.locale, clients.config.i18n.displayTimeZone),
        stateLabel: issue.isOpen ? "open" : "closed",
        pendingDependencySyncLabel: canonicalWorkflowState?.pendingDependencySyncLabel ?? "-",
        dependencySyncHistoryLabel: canonicalWorkflowState?.dependencySyncHistoryLabel ?? "-",
        activeOverrideDetails: (0, issue_report_1.formatActiveOverrideDetails)(canonicalWorkflowState?.configOverrides ?? null),
        originIssues,
        childIssues,
    };
}
async function loadIssueShowSourceData(clients, inputOptions) {
    const options = (0, issue_show_options_1.resolveIssueShowOptions)(inputOptions);
    const issue = await clients.ticketTracker.getIssueRecord(options.issueId);
    const workflowState = (0, workflow_labels_1.parseWorkflowStateFromLabelNames)(clients.workflowLabels, issue.labelNames ?? []);
    const [comments, pr, blockingDependencies] = await Promise.all([
        (0, shared_1.loadSortedIssueComments)(clients.ticketTracker, issue.id),
        options.prMode === issue_show_options_1.ISSUE_SHOW_PR_MODES.none
            ? Promise.resolve(null)
            : (0, issue_related_pull_request_1.loadRelatedPullRequest)(clients, issue, {
                includePullRequestTarget: true,
            }),
        clients.ticketTracker.listBlockingDependencies(issue.id),
    ]);
    const [prDetails, postCompletionCommentCandidates] = await Promise.all([
        (0, issue_show_pr_details_1.loadPullRequestDetails)({
            repositoryHost: clients.repositoryHost,
            pr,
            options,
        }),
        options.commentsMode === issue_show_options_1.ISSUE_SHOW_COMMENT_MODES.postCompletion
            ? (0, issue_show_comments_1.loadPostCompletionCommentCandidates)({
                ticketTracker: clients.ticketTracker,
                issue,
                issueComments: comments,
                pr,
            })
            : Promise.resolve({ issueComments: [], prComments: [] }),
    ]);
    const latestUpdate = findLatestParsedWorkflowUpdate(comments);
    const canonicalResolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(comments, clients.locale);
    const canonicalWorkflowState = resolveCanonicalWorkflowStateForShow(canonicalResolution, clients.locale, clients.config.i18n.displayTimeZone, issue.id);
    const foundWorkflowState = canonicalResolution.kind === "found"
        ? canonicalResolution.workflowState
        : null;
    return {
        options,
        issue,
        workflowState,
        comments,
        postCompletionCommentCandidates,
        pr,
        blockingDependencies,
        prDetails,
        latestUpdate,
        canonicalResolution,
        canonicalWorkflowState,
        foundWorkflowState,
    };
}
async function loadIssueShowChildIssuesViewModel(input) {
    const direct = await loadDirectIssueShowChildIssuesViewModel(input);
    if (input.remainingDepth === 1) {
        return direct;
    }
    const childIds = collectChildIssueIds(direct, input.visitedIssueIds);
    const descendantGroups = [];
    let hiddenCount = direct.hiddenCount;
    for (const childId of childIds) {
        const childIssue = await tryLoadIssueRecord(input.clients.ticketTracker, childId);
        if (childIssue === null) {
            continue;
        }
        input.visitedIssueIds.add(childIssue.id);
        const childWorkflowState = await loadCanonicalWorkflowStateForIssue(input.clients.ticketTracker, childIssue.id, input.clients.locale);
        const descendants = await loadIssueShowChildIssuesViewModel({
            clients: input.clients,
            options: input.options,
            parentIssue: childIssue,
            workflowState: childWorkflowState,
            remainingDepth: input.remainingDepth === null ? null : input.remainingDepth - 1,
            currentDepth: input.currentDepth + 1,
            visitedIssueIds: input.visitedIssueIds,
            relatedLimitBudget: input.relatedLimitBudget,
        });
        descendantGroups.push(...descendants.groups);
        hiddenCount += descendants.hiddenCount;
    }
    return {
        requested: true,
        groups: mergeChildIssueGroups([...direct.groups, ...descendantGroups]),
        hiddenCount,
    };
}
async function loadDirectIssueShowChildIssuesViewModel(input) {
    const blockingDependencies = input.blockingDependencies ??
        (await input.clients.ticketTracker.listBlockingDependencies(input.parentIssue.id));
    const [dependencyWorkflowStates, recurringHistory, followupIssues] = await Promise.all([
        (0, child_workflow_state_1.loadChildCanonicalStateMap)({
            ticketTracker: input.clients.ticketTracker,
            issues: blockingDependencies,
        }),
        loadRecurringChildHistory({
            ticketTracker: input.clients.ticketTracker,
            parentIssue: input.parentIssue,
            workflowState: input.workflowState,
        }),
        loadFollowupPublicationIssues({
            ticketTracker: input.clients.ticketTracker,
            workflowState: input.workflowState,
        }),
    ]);
    return (0, issue_rendering_1.buildIssueShowChildIssuesViewModel)({
        parentIssue: {
            id: input.parentIssue.id,
            displayId: input.parentIssue.displayId,
            url: input.parentIssue.url,
        },
        workflowState: input.workflowState,
        dependencies: blockingDependencies,
        dependencyWorkflowStates,
        recurringHistory,
        followupIssues,
        limit: input.options.relatedLimit,
        limitBudget: input.relatedLimitBudget,
        requested: true,
        depth: input.currentDepth,
    });
}
function collectChildIssueIds(childIssues, visitedIssueIds) {
    return childIssues.groups.flatMap((group) => group.items.flatMap((item) => item.id !== null && !visitedIssueIds.has(item.id) ? [item.id] : []));
}
function mergeChildIssueGroups(groups) {
    const byKind = new Map();
    for (const group of groups) {
        const existing = byKind.get(group.kind);
        if (existing === undefined) {
            byKind.set(group.kind, { kind: group.kind, items: [...group.items] });
            continue;
        }
        existing.items.push(...group.items);
    }
    return [...byKind.values()];
}
async function tryLoadIssueRecord(ticketTracker, issueId) {
    try {
        return await ticketTracker.getIssueRecord(issueId);
    }
    catch {
        return null;
    }
}
async function loadCanonicalWorkflowStateForIssue(ticketTracker, issueId, locale) {
    const comments = await (0, shared_1.loadSortedIssueComments)(ticketTracker, issueId);
    const resolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(comments, locale);
    return resolution.kind === "found" ? resolution.workflowState : null;
}
function compareIssueRecordsByUpdatedAtDesc(left, right) {
    const updatedComparison = right.updatedAt.localeCompare(left.updatedAt);
    if (updatedComparison !== 0) {
        return updatedComparison;
    }
    return right.id.localeCompare(left.id);
}
function buildPostCompletionCommentsViewModel(input) {
    const completedAt = resolveCompletedAt(input.issue, input.pr);
    if (input.mode !== issue_show_options_1.ISSUE_SHOW_COMMENT_MODES.postCompletion) {
        return {
            mode: input.mode,
            completedAt,
            comments: [],
            unavailable: false,
        };
    }
    if (completedAt === null) {
        return {
            mode: input.mode,
            completedAt: null,
            comments: [],
            unavailable: true,
        };
    }
    return {
        mode: input.mode,
        completedAt,
        comments: selectPostCompletionCommentsByTarget(input),
        unavailable: false,
    };
}
function selectPostCompletionCommentsByTarget(input) {
    const comments = [
        ...(input.issue.completedAt === undefined ||
            input.issue.completedAt === null
            ? []
            : (0, post_completion_comments_1.selectPostCompletionUserComments)({
                comments: input.candidates.issueComments,
                completedAt: input.issue.completedAt,
            })),
        ...(input.pr?.completedAt === undefined || input.pr.completedAt === null
            ? []
            : (0, post_completion_comments_1.selectPostCompletionUserComments)({
                comments: input.candidates.prComments,
                completedAt: input.pr.completedAt,
            })),
    ];
    return comments.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
}
function resolveDisplayedComments(input) {
    switch (input.mode) {
        case issue_show_options_1.ISSUE_SHOW_COMMENT_MODES.none:
        case issue_show_options_1.ISSUE_SHOW_COMMENT_MODES.postCompletion:
            return [];
        case issue_show_options_1.ISSUE_SHOW_COMMENT_MODES.all:
            return [...input.comments];
    }
}
function resolveCompletedAt(issue, pr) {
    return issue.completedAt ?? pr?.completedAt ?? null;
}
async function loadIssueParentChainViewModel(input) {
    const items = [];
    const visitedIssueIds = new Set([input.currentIssue.id]);
    let cursor = resolveParentCursor({
        issue: input.currentIssue,
        workflowState: input.workflowState,
    });
    while (cursor !== null &&
        (input.depth === null || items.length < input.depth)) {
        if (visitedIssueIds.has(cursor.issueId)) {
            break;
        }
        visitedIssueIds.add(cursor.issueId);
        const loaded = await loadIssueParentChainItem({
            ticketTracker: input.ticketTracker,
            cursor,
            locale: input.locale,
            displayTimeZone: input.displayTimeZone,
        });
        items.push(loaded.item);
        cursor = resolveParentCursor({
            issue: loaded.issue,
            workflowState: loaded.workflowState,
        });
    }
    return {
        items,
        truncated: cursor !== null && !visitedIssueIds.has(cursor.issueId),
    };
}
async function loadIssueParentChainItem(input) {
    try {
        const issue = await input.ticketTracker.getIssueRecord(input.cursor.issueId);
        const comments = await (0, shared_1.loadSortedIssueComments)(input.ticketTracker, issue.id);
        const resolution = (0, canonical_workflow_state_1.resolveCanonicalWorkflowStateComment)(comments, input.locale);
        const workflowState = resolution.kind === "found" ? resolution.workflowState : null;
        const summary = workflowState === null
            ? null
            : (0, canonical_workflow_state_1.summarizeCanonicalWorkflowState)(workflowState, input.locale, input.displayTimeZone, issue.id).currentStep;
        return {
            item: {
                displayId: issue.displayId,
                title: issue.title,
                url: issue.url,
                state: issue.isOpen ? "open" : "closed",
                requestTitle: input.cursor.requestTitle,
                summary,
            },
            issue,
            workflowState,
        };
    }
    catch {
        return {
            item: {
                displayId: input.cursor.displayId,
                title: (0, catalog_1.translate)(input.locale, "issue.command.show.originIssues.unavailableTitle"),
                url: input.cursor.url,
                state: "unknown",
                requestTitle: input.cursor.requestTitle,
                summary: null,
            },
            issue: {
                id: input.cursor.issueId,
                displayId: input.cursor.displayId,
                title: "",
            },
            workflowState: null,
        };
    }
}
function resolveParentCursor(input) {
    const originLink = input.workflowState?.originLink;
    if (originLink !== undefined) {
        return {
            issueId: originLink.sourceIssueId,
            displayId: originLink.sourceIssueDisplayId,
            url: originLink.sourceIssueUrl,
            requestTitle: originLink.followupRequestTitle ?? null,
        };
    }
    const parentLink = input.workflowState?.parentLink;
    if (parentLink !== undefined) {
        const parentIssueId = (0, issue_ref_1.normalizeWorkflowIssueDisplayRef)(parentLink.parentRef);
        if (parentIssueId !== null) {
            return {
                issueId: parentIssueId,
                displayId: parentLink.parentRef,
                url: null,
                requestTitle: null,
            };
        }
    }
    if ((0, recurring_1.hasRecurringChildIssueTitlePrefix)(input.issue.title)) {
        const parentIssueId = (0, child_issue_title_1.parseParentIssueIdTitleSuffix)(input.issue.title);
        if (parentIssueId !== null &&
            (0, recurring_child_dependencies_1.matchesRecurringChildIssueForParent)({
                title: input.issue.title,
                parentDisplayId: parentIssueId,
            })) {
            return {
                issueId: parentIssueId,
                displayId: parentIssueId.startsWith("#")
                    ? parentIssueId
                    : `#${parentIssueId}`,
                url: null,
                requestTitle: null,
            };
        }
    }
    return null;
}
async function loadRecurringChildHistory(input) {
    if (!(0, recurring_1.isRecurringParentWorkflowState)(input.workflowState)) {
        return [];
    }
    const candidates = await input.ticketTracker.searchIssueRecords({
        state: "all",
        mode: "title",
        text: input.parentIssue.displayId,
        limit: null,
        includePullRequests: false,
    });
    return candidates.filter((candidate) => candidate.id !== input.parentIssue.id &&
        candidate.kind === "issue" &&
        (0, recurring_child_dependencies_1.matchesRecurringChildIssueForParent)({
            title: candidate.title,
            parentDisplayId: input.parentIssue.displayId,
        }));
}
async function loadFollowupPublicationIssues(input) {
    const publications = input.workflowState?.followup?.publications ?? [];
    const issueIds = publications.flatMap((publication) => publication.status === "created" || publication.status === "reused"
        ? [publication.issueId]
        : []);
    const issues = [];
    for (const issueId of issueIds) {
        try {
            issues.push(await input.ticketTracker.getIssueRecord(issueId));
        }
        catch {
            // Missing follow-up issue records should not hide the source issue view.
        }
    }
    return issues.sort(compareIssueRecordsByUpdatedAtDesc);
}
function resolveCanonicalWorkflowStateForShow(resolution, locale, displayTimeZone, issueId) {
    if (resolution.kind === "missing") {
        return null;
    }
    if (resolution.kind === "invalid") {
        return (0, issue_report_1.buildInvalidCanonicalWorkflowStateView)({
            summary: resolution.summary,
            comment: resolution.comment,
            locale,
        });
    }
    const summary = (0, canonical_workflow_state_1.summarizeCanonicalWorkflowState)(resolution.workflowState, locale, displayTimeZone, issueId);
    return {
        summary: summary.currentStep,
        next: summary.next,
        workflowSurface: resolution.workflowState.workflowSurface,
        workflowTemplates: resolution.workflowState.workflowTemplates,
        followupProposalPolicy: resolution.workflowState.followup?.proposalPolicy ?? "off",
        followupProposalApproval: resolution.workflowState.followup?.proposalApproval ?? "auto",
        commentUrl: resolution.comment.url,
        resultSummary: summary.resultSummary,
        configOverrides: resolution.workflowState.configOverrides ?? null,
        ...(resolution.workflowState.autoMergeState !== undefined
            ? { autoMergeState: resolution.workflowState.autoMergeState }
            : {}),
        pendingDependencySyncLabel: (0, issue_report_1.formatPendingDependencySyncLabel)(resolution.workflowState.dependencySyncState),
        dependencySyncHistoryLabel: (0, issue_report_1.formatDependencySyncHistoryLabel)(resolution.workflowState.dependencySyncState),
    };
}
function resolveLatestUpdateHeadingLabel(latestUpdate, canonicalResolution, locale) {
    const canonicalComment = canonicalResolution.kind === "missing" ? null : canonicalResolution.comment;
    if (latestUpdate !== null &&
        canonicalComment !== null &&
        Date.parse(canonicalComment.updatedAt ?? canonicalComment.createdAt) >
            Date.parse(latestUpdate.comment.updatedAt ?? latestUpdate.comment.createdAt)) {
        return (0, catalog_1.translate)(locale, "issue.command.show.historicalUpdateHeading");
    }
    return (0, catalog_1.translate)(locale, "issue.command.show.latestUpdateHeading");
}
function findLatestParsedWorkflowUpdate(comments) {
    let latest = null;
    for (const comment of comments) {
        const parsed = (0, workflow_update_1.parseWorkflowUpdate)(comment.body);
        if (parsed) {
            latest = { comment, parsed };
        }
    }
    return latest;
}
