"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.maybeHandleRecurringParentIssue = maybeHandleRecurringParentIssue;
const catalog_1 = require("../../i18n/catalog");
const constants_1 = require("../../worker/constants");
const comment_command_1 = require("../../workflows/comment-command");
const publisher_shared_1 = require("../../workflows/publisher-shared");
const recurring_1 = require("../../workflows/recurring");
const recurring_auto_stop_1 = require("../../workflows/recurring-auto-stop");
const followup_policy_1 = require("../../workflows/followup-policy");
const state_1 = require("../../workflows/state");
const constants_2 = require("../constants");
const log_text_1 = require("../log-text");
const child_publication_1 = require("./child-publication");
const child_issue_title_1 = require("./child-issue-title");
const issue_creation_publication_1 = require("./issue-creation-publication");
const needs_human_1 = require("./needs-human");
const recurring_child_dependencies_1 = require("./recurring-child-dependencies");
const presentation_1 = require("./presentation");
const publisher_factory_1 = require("./publisher-factory");
const results_1 = require("./results");
const shared_1 = require("./shared");
const MAX_RECURRING_DEPENDENCY_CLEANUP_PER_RUN = 25;
const RECURRING_UPDATE_CHILD_TITLE_AFTER_PLAN_OVERRIDE_PATH = "recurring.updateChildTitleAfterPlan";
async function maybeHandleRecurringParentIssue(input) {
    if (!(0, recurring_1.hasActiveRecurringParentSchedule)(input.workflowState)) {
        return null;
    }
    const responseLanguage = input.context.config.i18n.defaultLanguage;
    const executedAt = new Date().toISOString();
    const nextWorkflowState = (0, needs_human_1.applyWorkflowExecutionMetadata)(input.workflowState, {
        executedAt,
        finalOutcome: constants_1.WORKER_OUTCOMES.proceed,
        recordsRunOutcome: true,
        shouldClearPendingPlanningIntent: true,
        pendingPlanningIntent: undefined,
        pendingPlanningReason: null,
        displayTimeZone: input.context.config.i18n.displayTimeZone,
    });
    const nextParentWorkflowState = buildRecurringParentWorkflowState({
        workflowState: nextWorkflowState,
    });
    const lifecycleDecision = (0, recurring_auto_stop_1.resolveRecurringLifecycleDecision)(input.workflowState);
    const hasRunLimitReached = (0, recurring_auto_stop_1.hasRecurringAutoStopRunLimitReached)(input.workflowState);
    if (lifecycleDecision === "stop_due") {
        const summaryKey = hasRunLimitReached
            ? "run.recurring.autoStop.runsSummary"
            : "run.recurring.autoStop.timeSummary";
        return publishRecurringAutoStoppedParent({
            ...input,
            publisher: (0, publisher_factory_1.createRunWorkflowPublisher)(input.context),
            workflowState: (0, recurring_auto_stop_1.buildStoppedRecurringParentWorkflowState)(nextParentWorkflowState),
            responseLanguage,
            summary: (0, catalog_1.translate)(responseLanguage, summaryKey),
            logReason: hasRunLimitReached
                ? "recurring auto-stop run limit reached"
                : "recurring auto-stop time reached",
            dependencyChanges: await buildAllRecurringDependencyCleanupChanges({
                context: input.context,
                parentIssue: input.executable.issue,
            }),
        });
    }
    const serviceAssignee = resolveRecurringChildAssignee(input.assignees, input.context.currentUserLogin);
    return publishRecurringChildCycle({
        runInput: input,
        workflowState: input.workflowState,
        executedAt,
        nextParentWorkflowState,
        serviceAssignee,
        responseLanguage,
    });
}
async function publishRecurringChildCycle(input) {
    const runInput = input.runInput;
    const recurringChildIssuePlanning = await buildRecurringChildIssuePlan({
        context: runInput.context,
        parentIssue: runInput.executable.issue,
        workflowState: input.workflowState,
        executedAt: input.executedAt,
        serviceAssignee: input.serviceAssignee,
        responseLanguage: input.responseLanguage,
        workflowLabels: runInput.workflowLabels,
    });
    const publisher = (0, publisher_factory_1.createRunWorkflowPublisher)(runInput.context);
    const recurringChildIssueResult = await resolveRecurringChildIssueResult({
        publisher,
        context: runInput.context,
        plan: recurringChildIssuePlanning.plan,
        responseLanguage: input.responseLanguage,
    });
    const parentPublicationState = resolveRecurringParentPublicationState({
        parentWorkflowState: input.nextParentWorkflowState,
        childIssueResult: recurringChildIssueResult,
        readySnapshot: runInput.claimedIssue.aiReadySnapshot,
    });
    await publisher.publishWorkflow((0, child_publication_1.buildChildParentPublicationRequest)({
        issue: runInput.executable.issue,
        workflowState: parentPublicationState.workflowState,
        projection: {
            previous: runInput.claimedIssue.aiRunningSnapshot,
            next: parentPublicationState.projection.snapshot,
            previousNeedsHumanReason: null,
            nextNeedsHumanReason: null,
        },
        workflowLabels: runInput.workflowLabels,
        responseLanguage: input.responseLanguage,
        visibleUpdate: null,
        dependencyChanges: buildRecurringDependencyCleanupChanges({
            parentIssueId: runInput.executable.issue.id,
            dependencies: recurringChildIssuePlanning.closedRecurringDependencies,
        }),
        commentCursorPolicy: runInput.scannedInputCursor === null
            ? { mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve }
            : {
                mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToScannedInput,
                scannedInputCursor: runInput.scannedInputCursor,
            },
    }));
    await appendRecurringChildRunLog({
        logger: runInput.logger,
        workflowLabels: runInput.workflowLabels,
        from: runInput.claimedIssue.aiRunningSnapshot.status,
        to: parentPublicationState.projection.snapshot.status,
        workflowSnapshot: parentPublicationState.projection.snapshot,
        recurringChildIssueResult,
    });
    return (0, results_1.buildRunCliSummary)({
        issue: runInput.executable.issue,
        outcome: constants_2.RUN_ISSUE_OUTCOMES.queuedAi,
        responseLanguage: input.responseLanguage,
        summary: recurringChildIssueResult.summary,
        commentUrl: null,
        prUrl: runInput.pullRequestUrl,
        nextStep: (0, catalog_1.translate)(input.responseLanguage, "run.recurring.nextStep.wait"),
    });
}
function resolveRecurringParentPublicationState(input) {
    const workflowState = input.childIssueResult.didCreate
        ? (0, recurring_auto_stop_1.incrementRecurringChildCreationCount)(input.parentWorkflowState)
        : input.parentWorkflowState;
    return {
        workflowState,
        projection: (0, recurring_1.resolveRecurringProjection)({
            snapshot: input.readySnapshot,
            workflowState,
        }),
    };
}
async function appendRecurringChildRunLog(input) {
    await input.logger.appendSection((0, log_text_1.issueLogSection)("resolvedTransition"), [
        (0, presentation_1.renderStatusTransitionSection)({
            workflowLabels: input.workflowLabels,
            from: input.from,
            to: input.to,
            facts: shared_1.EMPTY_FACTS,
            note: "recurring_child_gate",
            locale: input.logger.locale,
        }),
        (0, log_text_1.issueLogField)("workflow", (0, presentation_1.formatWorkflowState)(input.workflowLabels, input.workflowSnapshot)),
        (0, log_text_1.issueLogField)("reason", input.recurringChildIssueResult.logReason),
        (0, log_text_1.issueLogField)("issueUrl", input.recurringChildIssueResult.childIssue.url ?? log_text_1.ISSUE_LOG_TEXT.none),
    ].join("\n"));
    await input.logger.appendSection((0, log_text_1.issueLogSection)("finalLabelsCloseResult"), (0, presentation_1.renderFinalWorkflowStateSection)(input.workflowSnapshot, false, input.workflowLabels, input.logger.locale));
}
async function publishRecurringAutoStoppedParent(input) {
    await input.publisher.publishWorkflow((0, child_publication_1.buildChildParentPublicationRequest)({
        issue: input.executable.issue,
        workflowState: input.workflowState,
        projection: {
            previous: input.claimedIssue.aiRunningSnapshot,
            next: input.claimedIssue.aiRunningSnapshot,
            previousNeedsHumanReason: null,
            nextNeedsHumanReason: null,
            labelProjection: { omitWorkflowLabels: true },
        },
        workflowLabels: input.workflowLabels,
        responseLanguage: input.responseLanguage,
        visibleUpdate: null,
        terminalAction: {
            kind: "close",
            postCloseDependencyChanges: input.dependencyChanges,
        },
        commentCursorPolicy: input.scannedInputCursor === null
            ? { mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve }
            : {
                mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.advanceToScannedInput,
                scannedInputCursor: input.scannedInputCursor,
            },
    }));
    await input.logger.appendSection((0, log_text_1.issueLogSection)("resolvedTransition"), [
        (0, presentation_1.renderStatusTransitionSection)({
            workflowLabels: input.workflowLabels,
            from: input.claimedIssue.aiRunningSnapshot.status,
            to: input.claimedIssue.aiRunningSnapshot.status,
            facts: shared_1.EMPTY_FACTS,
            note: "recurring_auto_stop",
            locale: input.logger.locale,
        }),
        (0, log_text_1.issueLogField)("workflow", (0, presentation_1.formatWorkflowState)(input.workflowLabels, input.claimedIssue.aiRunningSnapshot)),
        (0, log_text_1.issueLogField)("reason", input.logReason),
    ].join("\n"));
    await input.logger.appendSection((0, log_text_1.issueLogSection)("finalLabelsCloseResult"), (0, presentation_1.renderFinalWorkflowStateSection)(input.claimedIssue.aiRunningSnapshot, true, input.workflowLabels, input.logger.locale));
    return (0, results_1.buildRunCliSummary)({
        issue: input.executable.issue,
        outcome: constants_2.RUN_ISSUE_OUTCOMES.queuedAi,
        responseLanguage: input.responseLanguage,
        summary: input.summary,
        commentUrl: null,
        prUrl: input.pullRequestUrl,
        nextStep: (0, catalog_1.translate)(input.responseLanguage, "run.recurring.autoStop.nextStep"),
    });
}
async function buildRecurringChildIssuePlan(input) {
    const dependencies = await input.context.ticketTracker.listBlockingDependencies(input.parentIssue.id);
    const activeRecurringChildDependencies = (0, recurring_child_dependencies_1.findActiveRecurringChildDependencies)({
        dependencies,
        parentDisplayId: input.parentIssue.displayId,
    });
    const closedRecurringDependencies = findClosedRecurringDependencies(dependencies, input.parentIssue.displayId);
    const activeChildLimit = input.context.config.recurring.maxActiveChildren;
    const existingChildIssue = activeRecurringChildDependencies[0];
    if (existingChildIssue !== undefined &&
        activeRecurringChildDependencies.length >= activeChildLimit) {
        return {
            plan: {
                kind: "reuse",
                issue: existingChildIssue,
            },
            closedRecurringDependencies,
        };
    }
    return {
        plan: {
            kind: "create",
            publicationPlan: (0, issue_creation_publication_1.buildCreateIssuePublicationPlan)({
                source: "recurring",
                branchTarget: "effective_base_branch",
                relationship: {
                    kind: "recurring_cycle",
                    parentIssueId: input.parentIssue.id,
                },
                title: buildRecurringChildIssueTitle(input.parentIssue, input.executedAt, input.responseLanguage),
                body: buildRecurringChildIssueBody(input.parentIssue, input.executedAt, input.responseLanguage),
                trackerFields: {
                    assignees: [input.serviceAssignee],
                    labelNames: [input.workflowLabels.aiReady],
                    ...(input.parentIssue.milestoneRef !== undefined
                        ? { milestoneRef: input.parentIssue.milestoneRef }
                        : {}),
                    ...(input.parentIssue.projectRef !== undefined
                        ? { projectRef: input.parentIssue.projectRef }
                        : {}),
                },
                workflowState: buildRecurringChildWorkflowState({
                    workflowState: input.workflowState,
                    followupConfig: input.context.config.followup,
                }),
                dependencyChanges: [
                    {
                        kind: "add_blocking_dependency",
                        issue: { kind: "issue", issueId: input.parentIssue.id },
                        blockedBy: { kind: "target" },
                    },
                ],
                newIssueFailurePolicy: "close_created_internal_issue",
                responseLanguage: input.responseLanguage,
            }),
        },
        closedRecurringDependencies,
    };
}
async function resolveRecurringChildIssueResult(input) {
    if (input.plan.kind === "reuse") {
        return {
            summary: (0, catalog_1.translate)(input.responseLanguage, "run.recurring.child.existsSummary", {
                childIssueDisplayId: input.plan.issue.displayId,
            }),
            logReason: "open child issue があるため skip",
            childIssue: input.plan.issue,
            didCreate: false,
        };
    }
    const publication = await (0, issue_creation_publication_1.executeIssuePublicationPlans)({
        publisher: input.publisher,
        plans: [input.plan.publicationPlan],
        eventHooks: {
            repoRoot: input.context.workflowTemplateRepoRoot ?? input.context.cwd,
            config: input.context.config,
            worker: input.context.worker,
            ...(input.context.logHomeDirectory !== undefined
                ? { homeDirectory: input.context.logHomeDirectory }
                : {}),
        },
    });
    const childIssue = publication.executions[0]?.issue;
    if (childIssue === undefined) {
        throw new Error("Recurring child publication returned no target issue.");
    }
    return {
        summary: (0, catalog_1.translate)(input.responseLanguage, "run.recurring.child.createdSummary", {
            childIssueDisplayId: childIssue.displayId,
        }),
        logReason: "child issue spawned",
        childIssue,
        didCreate: true,
    };
}
function resolveRecurringChildAssignee(assignees, currentUserLogin) {
    const assignee = currentUserLogin?.trim() || assignees[0] || "";
    if (assignee.length === 0) {
        throw new Error("Recurring child issue requires a single service assignee.");
    }
    return assignee;
}
function buildRecurringChildIssueTitle(parentIssue, executedAt, responseLanguage) {
    return (0, child_issue_title_1.appendParentIssueTitleSuffix)(`${(0, catalog_1.translate)(responseLanguage, "run.recurring.child.titlePrefix")} ${parentIssue.baseTitle} (${formatRecurringChildExecutedAt(executedAt)})`, parentIssue.displayId);
}
function formatRecurringChildExecutedAt(executedAt) {
    const formatter = new Intl.DateTimeFormat("en-CA", {
        timeZone: "Asia/Tokyo",
        year: "numeric",
        month: "2-digit",
        day: "2-digit",
        hour: "2-digit",
        minute: "2-digit",
        hour12: false,
    });
    const parts = new Map(formatter
        .formatToParts(new Date(executedAt))
        .map((part) => [part.type, part.value]));
    return `${parts.get("year")}-${parts.get("month")}-${parts.get("day")} ${parts.get("hour")}:${parts.get("minute")} JST`;
}
function buildRecurringChildIssueBody(parentIssue, executedAt, responseLanguage) {
    const brief = resolveRecurringChildBrief(parentIssue.body);
    const body = [
        `${(0, catalog_1.translate)(responseLanguage, "run.recurring.child.originIssueLabel")}: ${parentIssue.displayId}`,
        `${(0, catalog_1.translate)(responseLanguage, "run.recurring.child.executedAtLabel")}: ${formatRecurringChildExecutedAt(executedAt)}`,
    ];
    if (brief.length > 0) {
        body.push("", brief);
    }
    return body.join("\n");
}
function resolveRecurringChildBrief(parentIssueBody) {
    const parsed = (0, comment_command_1.parseWorkflowCommandComment)(parentIssueBody);
    if (parsed.kind !== "none") {
        return trimSurroundingBlankLines(parsed.bodyAfterCommand);
    }
    return trimSurroundingBlankLines(parentIssueBody);
}
function trimSurroundingBlankLines(value) {
    return value.replace(/^\s*\n/u, "").replace(/\n\s*$/u, "");
}
function findClosedRecurringDependencies(dependencies, parentDisplayId) {
    return dependencies
        .filter((dependency) => !dependency.isOpen &&
        (0, recurring_child_dependencies_1.isSameParentRecurringChildDependency)({
            dependency,
            parentDisplayId,
        }))
        .slice(0, MAX_RECURRING_DEPENDENCY_CLEANUP_PER_RUN);
}
function findRecurringDependencies(dependencies, parentDisplayId) {
    return dependencies.filter((dependency) => (0, recurring_child_dependencies_1.isSameParentRecurringChildDependency)({
        dependency,
        parentDisplayId,
    }));
}
async function buildAllRecurringDependencyCleanupChanges(input) {
    const dependencies = await input.context.ticketTracker.listBlockingDependencies(input.parentIssue.id);
    return buildRecurringDependencyCleanupChanges({
        parentIssueId: input.parentIssue.id,
        dependencies: findRecurringDependencies(dependencies, input.parentIssue.displayId),
    });
}
function buildRecurringDependencyCleanupChanges(input) {
    const dependencyIds = [...new Set(input.dependencies.map(({ id }) => id))];
    return dependencyIds.map((dependencyId) => ({
        kind: "remove_blocking_dependency",
        issue: { kind: "issue", issueId: input.parentIssueId },
        blockedBy: { kind: "issue", issueId: dependencyId },
    }));
}
function buildRecurringChildWorkflowState(input) {
    const { workflowState } = input;
    return (0, state_1.buildPersistedWorkflowState)({
        workflowSurface: workflowState.workflowSurface,
        workflowTemplates: workflowState.workflowTemplates,
        baseBranch: workflowState.baseBranch,
        priorityRank: workflowState.priorityRank ?? null,
        configOverrides: buildRecurringChildConfigOverrides(workflowState.configOverrides),
        executionContract: workflowState.executionContract,
        followup: (0, followup_policy_1.resolveWorkflowFollowupState)({
            config: input.followupConfig,
        }),
        plan: null,
    });
}
function buildRecurringChildConfigOverrides(parentConfigOverrides) {
    return {
        entries: [
            ...(parentConfigOverrides?.entries.filter((entry) => entry.path !== RECURRING_UPDATE_CHILD_TITLE_AFTER_PLAN_OVERRIDE_PATH) ?? []),
            {
                path: RECURRING_UPDATE_CHILD_TITLE_AFTER_PLAN_OVERRIDE_PATH,
                op: "set",
                value: true,
            },
        ],
    };
}
function buildRecurringParentWorkflowState(input) {
    const nextState = (0, state_1.buildPersistedWorkflowState)({
        workflowSurface: input.workflowState.workflowSurface,
        workflowTemplates: input.workflowState.workflowTemplates,
        baseBranch: input.workflowState.baseBranch,
        priorityRank: input.workflowState.priorityRank ?? null,
        ...(input.workflowState.schedule !== undefined
            ? { schedule: input.workflowState.schedule }
            : {}),
        recurring: input.workflowState.recurring ?? { kind: "parent" },
        nextRunAt: input.workflowState.nextRunAt ?? null,
        lastRunAt: input.workflowState.lastRunAt ?? null,
        lastOutcome: input.workflowState.lastOutcome ?? null,
        executionContract: input.workflowState.executionContract,
        plan: null,
        ...(input.workflowState.followup !== undefined
            ? { followup: input.workflowState.followup }
            : {}),
        ...(input.workflowState.configOverrides !== undefined
            ? {
                configOverrides: input.workflowState.configOverrides,
            }
            : {}),
    });
    nextState.consecutiveAiRuns = 0;
    nextState.totalAiRunHours = 0;
    nextState.lastScannedCommentCursor =
        input.workflowState.lastScannedCommentCursor ?? null;
    return nextState;
}
