"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildDisplayTree = buildDisplayTree;
exports.resolveWorkflowDisplayPhase = resolveWorkflowDisplayPhase;
exports.isWorkflowPlanningInProgress = isWorkflowPlanningInProgress;
exports.selectWorkflowPlanProgress = selectWorkflowPlanProgress;
exports.selectWorkflowPlanProgressFromSteps = selectWorkflowPlanProgressFromSteps;
const plan_progress_1 = require("../../workflows/plan-progress");
const recurring_1 = require("../../workflows/recurring");
const scheduled_run_1 = require("../../workflows/scheduled-run");
const state_1 = require("../../workflows/state");
const results_1 = require("./results");
function buildDisplayTree(workflowState) {
    const plan = workflowState.plan;
    if (plan === null || plan.steps.length === 0) {
        return {
            nodes: [],
            currentStepNode: null,
            currentPath: null,
            topLevelDoneCount: 0,
            topLevelTotalCount: 0,
        };
    }
    const progress = selectWorkflowPlanProgress(plan);
    const displayPhase = resolveWorkflowDisplayPhase(workflowState);
    const pauseCurrentStep = displayPhase === "foreign_approval" ||
        displayPhase === "start_approval" ||
        displayPhase === "pending";
    const rootNodes = [];
    const containerMap = new Map();
    const subtreeDisplayByKey = buildSubtreeDisplayLookup(plan.steps);
    for (const [index, step] of plan.steps.entries()) {
        const stepNode = {
            kind: "step",
            step,
            flatIndex: index,
            status: deriveActualStepStatus(index, plan, progress.currentStepIndex, pauseCurrentStep),
            isCurrent: progress.currentStepIndex === index,
            pathSegments: [],
            pathLabel: "",
        };
        if (!step.originSubtreeKey) {
            rootNodes.push(stepNode);
            continue;
        }
        let parentChildren = rootNodes;
        let currentKey = "";
        const segments = containerSegmentsForStep(step);
        for (const [depth, segment] of segments.entries()) {
            currentKey =
                currentKey.length === 0 ? segment : `${currentKey}>${segment}`;
            const containerDisplay = deriveContainerDisplay(step, segment, depth, segments.length, subtreeDisplayByKey);
            let container = containerMap.get(currentKey);
            if (!container) {
                container = {
                    kind: "subtree",
                    key: currentKey,
                    title: containerDisplay.title,
                    templateName: containerDisplay.templateName,
                    replannedReason: null,
                    status: "Pending",
                    doneCount: 0,
                    totalCount: 0,
                    startIndex: index,
                    endIndex: index,
                    children: [],
                    pathSegments: [],
                    pathLabel: "",
                };
                containerMap.set(currentKey, container);
                parentChildren.push(container);
            }
            applyContainerDisplay(container, containerDisplay);
            container.startIndex = Math.min(container.startIndex, index);
            container.endIndex = Math.max(container.endIndex, index);
            container.totalCount += 1;
            if (stepNode.status === "Done") {
                container.doneCount += 1;
            }
            parentChildren = container.children;
        }
        parentChildren.push(stepNode);
    }
    for (const container of containerMap.values()) {
        container.status = deriveContainerStatus(container.startIndex, container.endIndex, progress.currentStepIndex, plan, pauseCurrentStep);
        if (workflowState.pendingPlanningIntent === "replan" &&
            workflowState.pendingPlanningReason &&
            progress.currentStepIndex !== null &&
            plan.steps[progress.currentStepIndex]?.originSubtreeKey === container.key) {
            container.replannedReason = workflowState.pendingPlanningReason;
        }
    }
    let currentStepNode = null;
    let currentPath = null;
    let lastStepNode = null;
    let lastPath = null;
    assignDisplayPaths(rootNodes, [], (stepNode, ancestors) => {
        lastStepNode = stepNode;
        lastPath = [
            ...ancestors,
            `${stepNode.pathLabel} ${stepNode.step.title}`,
        ].join(" > ");
        if (stepNode.isCurrent) {
            currentStepNode = stepNode;
            currentPath = lastPath;
        }
    });
    return {
        nodes: rootNodes,
        currentStepNode: currentStepNode ?? lastStepNode,
        currentPath: currentPath ?? lastPath,
        topLevelDoneCount: rootNodes.filter((node) => node.status === "Done")
            .length,
        topLevelTotalCount: rootNodes.length,
    };
}
function resolveWorkflowDisplayPhase(workflowState) {
    if ((0, state_1.isWorkflowWaitingForForeignApproval)(workflowState)) {
        return "foreign_approval";
    }
    const plan = workflowState.plan;
    if (plan === null) {
        return (0, state_1.isWorkflowAwaitingStartApproval)(workflowState)
            ? "start_approval"
            : "pending";
    }
    const progress = selectWorkflowPlanProgress(plan);
    const currentStepResult = progress.currentStepIndex === null
        ? null
        : (0, results_1.coerceCanonicalWorkflowResult)(plan.steps[progress.currentStepIndex]?.result);
    if (currentStepResult?.outcome === "fail") {
        return "failed";
    }
    if ((0, state_1.isWorkflowAwaitingStartApproval)(workflowState) && !progress.isCompleted) {
        return "start_approval";
    }
    if (workflowState.lastOutcome === "wait_human" && !progress.isCompleted) {
        return "pending";
    }
    return progress.isCompleted ? "done" : "in_progress";
}
function isWorkflowPlanningInProgress(workflowState) {
    const plan = workflowState.plan;
    return (workflowState.executionOwnership !== undefined &&
        (plan === null || plan.steps.length === 0) &&
        !(0, state_1.isWorkflowWaitingForForeignApproval)(workflowState) &&
        !(0, state_1.isWorkflowAwaitingStartApproval)(workflowState) &&
        !(0, recurring_1.isRecurringParentWorkflowState)(workflowState) &&
        !(0, scheduled_run_1.hasActiveScheduledRun)(workflowState));
}
function selectWorkflowPlanProgress(plan) {
    if (plan === null) {
        return {
            currentStepIndex: null,
            completedStepCount: 0,
            totalStepCount: 0,
            isCompleted: false,
        };
    }
    return selectWorkflowPlanProgressFromSteps(plan.steps);
}
function selectWorkflowPlanProgressFromSteps(steps) {
    if (steps.length === 0) {
        return {
            currentStepIndex: null,
            completedStepCount: 0,
            totalStepCount: 0,
            isCompleted: false,
        };
    }
    const completedStepIds = (0, plan_progress_1.getCompletedWorkflowStepIds)(steps);
    const completedStepCount = steps.filter(plan_progress_1.isWorkflowStepDone).length;
    let failedStepIndex = null;
    let blockedStepIndex = null;
    for (const [index, step] of steps.entries()) {
        if ((0, plan_progress_1.isWorkflowStepFailed)(step)) {
            if (failedStepIndex === null) {
                failedStepIndex = index;
            }
            continue;
        }
        if ((0, plan_progress_1.isWorkflowStepBlocked)(step) && blockedStepIndex === null) {
            blockedStepIndex = index;
        }
    }
    const currentStepIndex = failedStepIndex ??
        blockedStepIndex ??
        findCurrentWorkflowStepIndex(steps, completedStepIds);
    return {
        currentStepIndex,
        completedStepCount,
        totalStepCount: steps.length,
        isCompleted: completedStepCount === steps.length && failedStepIndex === null,
    };
}
function containerSegmentsForStep(step) {
    const segments = step.originSubtreeKey?.split(">") ?? [];
    if (step.kind !== "workflow_ref") {
        return segments;
    }
    return segments.slice(0, -1);
}
function applyContainerDisplay(container, display) {
    if (container.title === null && display.title !== null) {
        container.title = display.title;
    }
    if (container.templateName === null && display.templateName !== null) {
        container.templateName = display.templateName;
    }
}
function buildSubtreeDisplayLookup(steps) {
    const lookup = new Map();
    for (const step of steps) {
        addSubtreeDisplayLookupEntry(lookup, step.callSiteId ?? step.expansionGroupId, step.targetTemplateName);
        addSubtreeDisplayLookupEntry(lookup, step.expansionGroupId, step.targetTemplateName);
        if (step.originWorkflowRef) {
            addSubtreeDisplayLookupEntry(lookup, step.originWorkflowRef.callSiteId, step.originWorkflowRef.targetTemplateName);
            addSubtreeDisplayLookupEntry(lookup, step.originWorkflowRef.expansionGroupId, step.originWorkflowRef.targetTemplateName);
        }
    }
    return lookup;
}
function addSubtreeDisplayLookupEntry(lookup, key, templateName) {
    if (key === undefined || templateName === undefined || lookup.has(key)) {
        return;
    }
    lookup.set(key, {
        title: formatWorkflowSubtreeTitle(templateName),
        templateName,
    });
}
function deriveContainerDisplay(step, segment, depth, totalDepth, lookup) {
    const fromLookup = lookup.get(segment);
    if (fromLookup !== undefined) {
        return fromLookup;
    }
    if (step.kind === "workflow_ref" && depth === totalDepth - 1) {
        return {
            title: step.sourceTemplateName ?? null,
            templateName: step.sourceTemplateName ?? null,
        };
    }
    if (step.originWorkflowRef && depth === totalDepth - 2) {
        return {
            title: step.originWorkflowRef.sourceTemplateName,
            templateName: step.originWorkflowRef.sourceTemplateName,
        };
    }
    return { title: null, templateName: null };
}
function formatWorkflowSubtreeTitle(templateName) {
    const fileName = templateName.split("/").at(-1) ?? templateName;
    const basename = fileName.endsWith(".md") ? fileName.slice(0, -3) : fileName;
    if (!/[-_]/u.test(basename)) {
        return templateName;
    }
    const words = basename
        .split(/[-_]+/u)
        .filter((word) => word.length > 0)
        .filter((word, index) => !(index === 0 && isWorkflowRolePrefix(word)));
    if (words.length === 0) {
        return templateName;
    }
    return words.map(formatWorkflowSubtreeTitleWord).join(" ");
}
function isWorkflowRolePrefix(word) {
    return word === "worker" || word === "owner" || word === "workflow";
}
function formatWorkflowSubtreeTitleWord(word) {
    switch (word.toLowerCase()) {
        case "ai":
            return "AI";
        case "api":
            return "API";
        case "css":
            return "CSS";
        case "figma":
            return "Figma";
        case "ga4":
            return "GA4";
        case "gsc":
            return "GSC";
        case "html":
            return "HTML";
        case "mcp":
            return "MCP";
        case "seo":
            return "SEO";
        case "ui":
            return "UI";
        case "url":
            return "URL";
        default:
            return `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`;
    }
}
function assignDisplayPaths(nodes, prefixSegments, onStep, ancestorLabels = []) {
    nodes.forEach((node, index) => {
        const pathSegments = [...prefixSegments, index + 1];
        node.pathSegments = pathSegments;
        node.pathLabel = pathSegments.join(".");
        if (node.kind === "subtree") {
            const title = node.title ?? node.key.split(">").at(-1) ?? node.key;
            assignDisplayPaths(node.children, pathSegments, onStep, [
                ...ancestorLabels,
                `${node.pathLabel} ${title}`,
            ]);
            return;
        }
        onStep(node, ancestorLabels);
    });
}
function deriveActualStepStatus(index, plan, currentStepIndex, pauseCurrentStep) {
    const step = plan.steps[index];
    if (!step) {
        return "Pending";
    }
    if ((0, plan_progress_1.isWorkflowStepFailed)(step)) {
        return "Failed";
    }
    if ((0, plan_progress_1.isWorkflowStepDone)(step)) {
        return "Done";
    }
    if ((0, plan_progress_1.isUnmaterializedIncompleteControllerChildIssueStep)(step)) {
        return "Pending";
    }
    if (currentStepIndex === index) {
        return pauseCurrentStep ? "Pending" : "In progress";
    }
    if ((0, plan_progress_1.isMaterializedIncompleteControllerChildIssueStep)(step)) {
        return pauseCurrentStep ? "Pending" : "In progress";
    }
    return "Pending";
}
function deriveContainerStatus(startIndex, endIndex, currentStepIndex, plan, pauseCurrentStep) {
    const steps = plan.steps.slice(startIndex, endIndex + 1);
    if (steps.some(plan_progress_1.isWorkflowStepFailed)) {
        return "Failed";
    }
    if (steps.every(plan_progress_1.isWorkflowStepDone)) {
        return "Done";
    }
    if (steps.some(plan_progress_1.isMaterializedIncompleteControllerChildIssueStep)) {
        return pauseCurrentStep ? "Pending" : "In progress";
    }
    if (currentStepIndex === null) {
        return "Pending";
    }
    if (currentStepIndex < startIndex) {
        return "Pending";
    }
    if (currentStepIndex > endIndex) {
        return "Done";
    }
    if ((0, plan_progress_1.isUnmaterializedIncompleteControllerChildIssueStep)(plan.steps[currentStepIndex])) {
        return "Pending";
    }
    return pauseCurrentStep ? "Pending" : "In progress";
}
function findCurrentWorkflowStepIndex(steps, completedStepIds) {
    let firstIncompleteStepIndex = null;
    for (const [index, step] of steps.entries()) {
        if ((0, plan_progress_1.isWorkflowStepDone)(step) || (0, plan_progress_1.isWorkflowStepFailed)(step)) {
            continue;
        }
        if (firstIncompleteStepIndex === null) {
            firstIncompleteStepIndex = index;
        }
        if (areWorkflowStepDependenciesSatisfied(step, completedStepIds)) {
            return index;
        }
    }
    if (firstIncompleteStepIndex !== null) {
        return firstIncompleteStepIndex;
    }
    return steps.length > 0 ? steps.length - 1 : null;
}
function areWorkflowStepDependenciesSatisfied(step, completedStepIds) {
    return step.dependsOnStepIds.every((dependencyId) => completedStepIds.has(dependencyId));
}
