"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildCreateIssuePublicationPlan = buildCreateIssuePublicationPlan;
exports.buildPatchIssuePublicationPlan = buildPatchIssuePublicationPlan;
exports.buildReuseIssuePublicationPlan = buildReuseIssuePublicationPlan;
exports.executeIssuePublicationPlans = executeIssuePublicationPlans;
const control_plane_config_1 = require("../../config/control-plane-config");
const runner_1 = require("../../event-hooks/runner");
const types_1 = require("../../platform/ticket-tracker/types");
const workflow_labels_1 = require("../../platform/ticket-tracker/workflow-labels");
const types_2 = require("../../platform/workflow-state/types");
const publish_request_builders_1 = require("../../workflows/publish-request-builders");
const publisher_shared_1 = require("../../workflows/publisher-shared");
const publisher_events_1 = require("../../workflows/publisher-events");
const publisher_state_support_1 = require("../../workflows/publisher-state-support");
const recurring_1 = require("../../workflows/recurring");
const run_limit_policy_1 = require("./run-limit-policy");
function buildCreateIssuePublicationPlan(input) {
    validateIssueCreationSpec(input);
    const workflowState = applyIssueCreationRunLimitPolicy({
        workflowState: input.workflowState,
        runLimitPolicy: input.runLimitPolicy,
    });
    return {
        kind: "create",
        issueKey: input.issueKey ?? null,
        request: (0, publish_request_builders_1.buildNewIssueWorkflowPublishRequest)({
            source: input.source,
            title: input.title,
            body: input.body,
            trackerFields: buildIssueCreationTrackerFields(applyIssueCreationRunLimitLabels({
                trackerFields: input.trackerFields,
                workflowState,
                runLimitPolicy: input.runLimitPolicy,
            })),
            workflowState,
            workflowProjection: input.workflowProjection,
            dependencyChanges: input.dependencyChanges,
            commentCursorPolicy: {
                mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve,
            },
            newIssueFailurePolicy: input.newIssueFailurePolicy,
            responseLanguage: input.responseLanguage,
        }),
    };
}
function validateIssueCreationSpec(input) {
    validateIssueCreationTitleAndBody(input);
    validateIssueCreationRelationship(input);
    validateIssueCreationBranchTarget(input);
    validateIssueCreationFailurePolicy(input);
    validateIssueCreationDependencyChanges(input);
    validateIssueCreationWorkflowState(input);
}
function validateIssueCreationTitleAndBody(input) {
    if (input.title.trim().length === 0 || input.body.trim().length === 0) {
        throw new Error("Issue creation requires non-empty title and body.");
    }
}
function validateIssueCreationRelationship(input) {
    const expectedRelationshipKind = resolveExpectedRelationshipKind(input.source);
    if (input.relationship.kind !== expectedRelationshipKind) {
        throw new Error(`Issue creation source ${input.source} requires relationship ${expectedRelationshipKind}.`);
    }
    switch (input.relationship.kind) {
        case "independent":
            return;
        case "followup_origin":
            assertNonEmptyRelationshipField({
                source: input.source,
                fieldName: "sourceIssueId",
                value: input.relationship.sourceIssueId,
            });
            return;
        case "blocking_parent":
        case "recurring_cycle":
            assertNonEmptyRelationshipField({
                source: input.source,
                fieldName: "parentIssueId",
                value: input.relationship.parentIssueId,
            });
            return;
        case "preflight_resolution":
            assertNonEmptyRelationshipField({
                source: input.source,
                fieldName: "parentIssueId",
                value: input.relationship.parentIssueId,
            });
            assertNonEmptyRelationshipField({
                source: input.source,
                fieldName: "resolutionKey",
                value: input.relationship.resolutionKey,
            });
            return;
    }
}
function assertNonEmptyRelationshipField(input) {
    if (input.value.trim().length === 0) {
        throw new Error(`Issue creation source ${input.source} requires non-empty relationship.${input.fieldName}.`);
    }
}
function validateIssueCreationFailurePolicy(input) {
    const expectedFailurePolicy = input.source === "cli"
        ? "leave_created_issue"
        : "close_created_internal_issue";
    if (input.newIssueFailurePolicy !== expectedFailurePolicy) {
        throw new Error(`Issue creation source ${input.source} requires failure policy ${expectedFailurePolicy}.`);
    }
}
function validateIssueCreationBranchTarget(input) {
    const expectedTargets = resolveExpectedBranchTargets(input.source);
    if (!expectedTargets.includes(input.branchTarget)) {
        throw new Error(`Issue creation source ${input.source} requires branch target ${expectedTargets.join(" or ")}.`);
    }
}
function validateIssueCreationDependencyChanges(input) {
    if (input.relationship.kind === "independent") {
        if ((input.dependencyChanges ?? []).length > 0) {
            throw new Error("Independent issue creation cannot include dependencies.");
        }
        return;
    }
    if (input.relationship.kind !== "blocking_parent" &&
        input.relationship.kind !== "recurring_cycle" &&
        input.relationship.kind !== "preflight_resolution") {
        return;
    }
    const parentIssueId = input.relationship.parentIssueId;
    const hasParentTargetBlocker = (input.dependencyChanges ?? []).some((change) => change.kind === "add_blocking_dependency" &&
        issueRefMatches(change.issue, parentIssueId) &&
        change.blockedBy.kind === "target");
    if (!hasParentTargetBlocker) {
        throw new Error(`Issue creation source ${input.source} must add a blocking dependency from parent issue ${parentIssueId} to the created issue.`);
    }
}
function validateIssueCreationWorkflowState(input) {
    if (input.relationship.kind === "independent") {
        return;
    }
    if (input.workflowState === null) {
        throw new Error(`Issue creation source ${input.source} requires workflow state.`);
    }
}
function issueRefMatches(ref, issueId) {
    return ref.kind === "issue" && ref.issueId === issueId;
}
function resolveExpectedRelationshipKind(source) {
    switch (source) {
        case "cli":
            return "independent";
        case "followup":
            return "followup_origin";
        case "controller":
        case "unplanned":
            return "blocking_parent";
        case "recurring":
            return "recurring_cycle";
        case "preflight":
            return "preflight_resolution";
    }
}
function resolveExpectedBranchTargets(source) {
    switch (source) {
        case "cli":
        case "followup":
        case "recurring":
        case "preflight":
            return ["effective_base_branch"];
        case "controller":
            return ["parent_issue_branch"];
        case "unplanned":
            return ["effective_base_branch", "parent_issue_branch"];
    }
}
function buildPatchIssuePublicationPlan(input) {
    const workflowState = applyIssueCreationRunLimitPolicy({
        workflowState: input.workflowState,
        runLimitPolicy: input.runLimitPolicy,
    });
    if (workflowState === null) {
        throw new Error("Issue patch publication requires workflow state.");
    }
    const trackerFields = buildIssueCreationTrackerFields({
        ...applyIssueCreationRunLimitLabels({
            trackerFields: input.trackerFields,
            workflowState,
            runLimitPolicy: input.runLimitPolicy,
        }),
        title: input.title,
        body: input.body,
    });
    return {
        kind: "patch",
        issueKey: input.issueKey ?? null,
        request: (0, publish_request_builders_1.buildExistingIssueWorkflowPublishRequest)({
            issueId: input.issue.id,
            preloadedIssue: input.issue,
            trackerFields,
            workflowState,
            workflowProjection: applyIssueCreationRunLimitProjection({
                issue: input.issue,
                workflowProjection: input.workflowProjection,
                workflowState,
                runLimitPolicy: input.runLimitPolicy,
            }),
            commentCursorPolicy: {
                mode: publisher_shared_1.WORKFLOW_COMMENT_CURSOR_POLICY_MODES.preserve,
            },
            responseLanguage: input.responseLanguage,
        }),
    };
}
function buildReuseIssuePublicationPlan(input) {
    return {
        kind: "reuse",
        issueKey: input.issueKey ?? null,
        issue: input.issue,
    };
}
async function executeIssuePublicationPlans(input) {
    const executions = [];
    const issueByKey = new Map();
    const activeIssueByKey = new Map();
    const createdIssues = [];
    const updatedIssues = [];
    for (const plan of input.plans) {
        const execution = plan.kind === "reuse"
            ? {
                kind: plan.kind,
                issueKey: plan.issueKey,
                issue: plan.issue,
                request: null,
                publication: null,
            }
            : {
                kind: plan.kind,
                issueKey: plan.issueKey,
                request: plan.request,
                publication: await requireIssuePublicationExecutor(input.publisher).publishWorkflow(plan.request),
            };
        const issue = execution.kind === "reuse"
            ? execution.issue
            : toPublishedIssue(execution.publication);
        executions.push({
            kind: execution.kind,
            issueKey: execution.issueKey,
            issue,
            request: execution.request,
            publication: execution.kind === "reuse" ? null : execution.publication,
        });
        if (execution.issueKey !== null) {
            issueByKey.set(execution.issueKey, issue);
            if (issue.isOpen) {
                activeIssueByKey.set(execution.issueKey, issue);
            }
        }
        if (execution.kind === "create") {
            createdIssues.push(issue);
        }
        else if (execution.kind === "patch") {
            updatedIssues.push(issue);
        }
    }
    if (input.eventHooks !== undefined) {
        await (0, runner_1.runEventHooksForPublications)({
            ...input.eventHooks,
            publications: executions.flatMap((execution) => execution.request !== null && execution.publication !== null
                ? [
                    {
                        request: execution.request,
                        result: execution.publication,
                        origin: execution.request.target.kind === "new_issue"
                            ? "issue_creation"
                            : "worker_publish",
                        run: null,
                    },
                ]
                : []),
        });
    }
    return {
        executions,
        issueByKey,
        activeIssueByKey,
        createdIssues,
        updatedIssues,
    };
}
function toPublishedIssue(result) {
    const workflowProjection = result.targetWorkflowProjection ??
        ((0, types_1.hasTrackerWorkflowState)(result.targetIssue)
            ? result.targetIssue.workflowState
            : null);
    if (workflowProjection === null) {
        throw new Error("Issue publication requires a workflow projection.");
    }
    const issue = {
        ...result.targetIssue,
        workflowState: {
            status: workflowProjection.status,
            rules: [...workflowProjection.rules],
        },
    };
    if (result.targetClosed) {
        issue.isOpen = false;
    }
    return issue;
}
function requireIssuePublicationExecutor(publisher) {
    if (publisher === undefined) {
        throw new Error("Issue publication executor is required.");
    }
    return publisher;
}
function buildIssueCreationTrackerFields(input) {
    const trackerFieldsInput = {
        assignees: input.assignees,
        labelNames: input.labelNames,
    };
    if (input.title !== undefined) {
        trackerFieldsInput.title = input.title;
    }
    if (input.body !== undefined) {
        trackerFieldsInput.body = input.body;
    }
    if (input.milestoneRef !== undefined) {
        trackerFieldsInput.milestoneRef = input.milestoneRef;
    }
    if (input.projectRef !== undefined) {
        trackerFieldsInput.projectRef = input.projectRef;
    }
    const trackerFields = (0, publisher_state_support_1.buildWorkflowTrackerFields)(trackerFieldsInput);
    if (trackerFields === undefined) {
        throw new Error("Issue creation tracker fields are required.");
    }
    return trackerFields;
}
function applyIssueCreationRunLimitPolicy(input) {
    if (input.workflowState === null || input.runLimitPolicy === undefined) {
        return input.workflowState;
    }
    const effectiveConfig = (0, control_plane_config_1.resolveIssueEffectiveConfigForWorkflowState)(input.runLimitPolicy.config, input.workflowState);
    if (!(0, run_limit_policy_1.resolveAiRunLimitPolicy)({
        config: effectiveConfig,
        consecutiveAiRuns: input.workflowState.consecutiveAiRuns,
        totalAiRunHours: input.workflowState.totalAiRunHours,
    }).reached) {
        return input.workflowState;
    }
    return {
        ...input.workflowState,
        needsHumanReason: publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit,
    };
}
function applyIssueCreationRunLimitLabels(input) {
    if (input.runLimitPolicy === undefined ||
        input.workflowState?.needsHumanReason !==
            publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit) {
        return input.trackerFields;
    }
    const workflowLabels = (0, workflow_labels_1.resolveWorkflowLabels)(input.runLimitPolicy.workflowLabels);
    const managedLabels = new Set((0, workflow_labels_1.getNonFollowupManagedLabelNames)(workflowLabels));
    const nonWorkflowLabels = input.trackerFields.labelNames.filter((labelName) => !managedLabels.has(labelName));
    return {
        ...input.trackerFields,
        labelNames: [
            ...nonWorkflowLabels,
            workflowLabels.aiNeedsHuman,
            workflowLabels.aiRunLimit,
        ],
    };
}
function applyIssueCreationRunLimitProjection(input) {
    if (input.workflowProjection === undefined ||
        input.runLimitPolicy === undefined ||
        input.workflowState.needsHumanReason !==
            publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit) {
        return input.workflowProjection;
    }
    const next = {
        ...input.workflowProjection.next,
        status: types_2.WORKFLOW_STATUSES.aiNeedsHuman,
    };
    return {
        ...input.workflowProjection,
        next,
        auxiliaryManagedLabels: {
            previous: (0, workflow_labels_1.pickAuxiliaryManagedLabelNames)(input.runLimitPolicy.workflowLabels, input.issue.labelNames),
            next: (0, workflow_labels_1.resolveAuxiliaryManagedLabelNames)({
                workflowLabels: input.runLimitPolicy.workflowLabels,
                snapshot: next,
                needsHumanReason: publisher_events_1.WORKFLOW_NEEDS_HUMAN_REASONS.runLimit,
                recurringLabelNames: (0, recurring_1.resolveRecurringLabelNames)(input.runLimitPolicy.workflowLabels.aiRecurring, input.workflowState),
            }),
        },
    };
}
