"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.workerWorkflowOptionalMetadataShape = exports.workerWorkflowOverridesSchema = void 0;
exports.buildWorkerWorkflowOptionPromptRules = buildWorkerWorkflowOptionPromptRules;
exports.validateWorkerWorkflowOptionSemanticRules = validateWorkerWorkflowOptionSemanticRules;
exports.buildWorkerWorkflowConfigOverridePatch = buildWorkerWorkflowConfigOverridePatch;
const zod_1 = require("zod");
const control_plane_config_1 = require("../config/control-plane-config");
const issue_override_option_registry_1 = require("../workflows/issue-override-option-registry");
const types_1 = require("../workflows/types");
const workflow_schedule_contract_support_1 = require("./workflow-schedule-contract-support");
const validation_issues_1 = require("./validation-issues");
const workflowStartOverridesSchema = zod_1.z
    .object({
    newPlan: zod_1.z
        .enum(control_plane_config_1.WORKFLOW_START_APPROVAL_MODE_VALUES)
        .nullable()
        .optional()
        .describe("Override workflow-start approval for new plans."),
    replan: zod_1.z
        .enum(control_plane_config_1.WORKFLOW_START_APPROVAL_MODE_VALUES)
        .nullable()
        .optional()
        .describe("Override workflow-start approval for replans."),
    scope: zod_1.z
        .enum(["issue_only", "issue_and_pr"])
        .nullable()
        .optional()
        .describe("Override workflow-start approval scope."),
})
    .strict()
    .describe("Workflow-start approval overrides.");
const aiRunLimitsOverridesSchema = zod_1.z
    .object({
    maxRuns: zod_1.z
        .number()
        .int()
        .min(0)
        .nullable()
        .optional()
        .describe("Override max AI runs before human review."),
    maxHours: zod_1.z
        .number()
        .min(0)
        .nullable()
        .optional()
        .describe("Override max AI run hours before human review; null turns it off."),
})
    .strict()
    .describe("AI run limit overrides.");
const recurringOverridesSchema = zod_1.z
    .object({
    maxActiveChildren: zod_1.z
        .number()
        .int()
        .min(1)
        .nullable()
        .optional()
        .describe("Override max open recurring child issues for the parent."),
})
    .strict()
    .describe("Recurring parent overrides.");
const followupMaxIssuesPerSourceOverridesSchema = zod_1.z
    .object({
    mainWork: zod_1.z
        .number()
        .int()
        .min(0)
        .max(5)
        .nullable()
        .optional()
        .describe("Override automatic mainWork follow-up issue cap."),
    maintenance: zod_1.z
        .number()
        .int()
        .min(0)
        .max(5)
        .nullable()
        .optional()
        .describe("Override automatic maintenance follow-up issue cap."),
})
    .strict()
    .describe("Follow-up recommendation category caps.");
const followupOverridesSchema = zod_1.z
    .object({
    proposalPolicy: zod_1.z
        .enum(types_1.FOLLOWUP_PROPOSAL_POLICIES)
        .nullable()
        .optional()
        .describe("Override whether follow-up proposals are generated."),
    proposalApproval: zod_1.z
        .enum(types_1.FOLLOWUP_PROPOSAL_APPROVALS)
        .nullable()
        .optional()
        .describe("Override whether follow-up proposals require approval."),
    maxIssuesPerSource: followupMaxIssuesPerSourceOverridesSchema
        .nullable()
        .optional()
        .describe("Override automatic follow-up issue caps by category."),
})
    .strict()
    .describe("Follow-up overrides.");
const pullRequestCompletionOverridesSchema = zod_1.z
    .object({
    reviewPolicy: zod_1.z
        .enum(control_plane_config_1.PULL_REQUEST_REVIEW_POLICY_VALUES)
        .nullable()
        .optional()
        .describe("Override whether pull request review runs."),
    mergePolicy: zod_1.z
        .enum(control_plane_config_1.PULL_REQUEST_MERGE_POLICY_VALUES)
        .nullable()
        .optional()
        .describe("Override whether pull requests are merged manually or automatically."),
})
    .strict()
    .describe("Pull request completion policy overrides.");
const issueCompletionOverridesSchema = zod_1.z
    .object({
    target: zod_1.z
        .enum(control_plane_config_1.ISSUE_COMPLETION_TARGET_VALUES)
        .nullable()
        .optional()
        .describe("Override where completed source issue work continues."),
    pullRequest: pullRequestCompletionOverridesSchema
        .nullable()
        .optional()
        .describe("Override pull request completion policies."),
})
    .strict()
    .describe("Issue completion overrides.");
exports.workerWorkflowOverridesSchema = zod_1.z
    .object({
    issueCompletion: issueCompletionOverridesSchema
        .nullable()
        .optional()
        .describe("Override what happens after workflow_closing confirms source issue completion."),
    aiRunLimits: aiRunLimitsOverridesSchema
        .nullable()
        .optional()
        .describe("Override AI run limits."),
    recurring: recurringOverridesSchema
        .nullable()
        .optional()
        .describe("Override recurring parent behavior."),
    followup: followupOverridesSchema
        .nullable()
        .optional()
        .describe("Override follow-up behavior."),
    defaultReviewers: zod_1.z
        .array(zod_1.z.string().min(1))
        .nullable()
        .optional()
        .describe("Override default pull request reviewers."),
    baseBranch: zod_1.z
        .string()
        .min(1)
        .nullable()
        .optional()
        .describe("Override repository base branch."),
    foreignIssueApprovalScope: zod_1.z
        .enum(["external_only", "all_foreign"])
        .nullable()
        .optional()
        .describe("Override foreign issue approval scope."),
    workflowStart: workflowStartOverridesSchema
        .nullable()
        .optional()
        .describe("Override workflow-start approval settings."),
})
    .strict()
    .describe("Structured config overrides.");
exports.workerWorkflowOptionalMetadataShape = {
    schedule: workflow_schedule_contract_support_1.workerWorkflowScheduleSchema
        .nullable()
        .optional()
        .describe("Recurring workflow schedule."),
    scheduledRun: workflow_schedule_contract_support_1.workerWorkflowScheduledRunSchema
        .nullable()
        .optional()
        .describe("One-shot run-at/run-after schedule command."),
    firstRun: workflow_schedule_contract_support_1.workerWorkflowFirstRunSchema
        .nullable()
        .optional()
        .describe("First run command for recurring schedules."),
    recurringAutoStop: workflow_schedule_contract_support_1.workerWorkflowRecurringAutoStopSchema
        .nullable()
        .optional()
        .describe("Recurring auto-stop policy."),
    overrides: exports.workerWorkflowOverridesSchema
        .nullable()
        .optional()
        .describe("Structured config overrides."),
};
function buildWorkerWorkflowOptionPromptRules(overridesRules) {
    return [
        ...overridesRules,
        "Set workflow.overrides.followup.proposalPolicy to if_needed when the prompt asks for independent post-completion iteration/improvement or a follow-up issue. The runtime will decide whether a follow-up issue is actually needed after completion.",
        "Set workflow.overrides.followup.proposalPolicy to off only when the prompt explicitly disables follow-up proposal or follow-up issue creation.",
        "Set workflow.overrides.followup.proposalApproval to confirm when the prompt asks to review all follow-up proposals before creating issues. Set it to confirm_if_needed when the prompt asks to create clearly necessary follow-ups automatically but review uncertain ones first. Omit it when no approval preference is requested.",
        "Do not use workflow.overrides.followup proposal fields for unmet doneWhen, broad unresolved goals, or repeated future runs that require workflow.schedule.",
        "Use workflow.overrides.followup.maxIssuesPerSource only when the prompt explicitly asks to change automatic follow-up issue caps. Values must be integers from 0 to 5; omit any category that should inherit the current config.",
        "workflow.overrides must not use the config inherit sentinel string; omit the field or use null instead.",
    ];
}
function validateWorkerWorkflowOptionSemanticRules(workflow, input) {
    const issues = [];
    (0, workflow_schedule_contract_support_1.validateWorkerWorkflowScheduleSemanticRules)(workflow, input, issues);
    validateStructuredOverrides(workflow.overrides, issues);
    return issues;
}
function buildWorkerWorkflowConfigOverridePatch(overrides) {
    if (overrides === undefined || overrides === null) {
        return null;
    }
    const defaultSentinelPath = findDefaultSentinelPath(overrides, [
        "workflow",
        "overrides",
    ]);
    if (defaultSentinelPath !== null) {
        throw new Error(`${formatIssuePath(defaultSentinelPath)} must not use the config inherit sentinel.`);
    }
    const entries = [];
    appendIssueCompletionOverrides(entries, overrides.issueCompletion);
    appendSetOverrideEntry(entries, {
        fieldPath: ["workflow", "overrides", "aiRunLimits", "maxRuns"],
        path: "aiRunLimits.maxRuns",
        expectedKind: "integer",
        value: overrides.aiRunLimits?.maxRuns,
    });
    if (overrides.aiRunLimits &&
        Object.hasOwn(overrides.aiRunLimits, "maxHours")) {
        const maxHoursPath = ["workflow", "overrides", "aiRunLimits", "maxHours"];
        if (overrides.aiRunLimits.maxHours === null) {
            appendOperationOverrideEntry(entries, {
                fieldPath: maxHoursPath,
                path: "aiRunLimits.maxHours",
                expectedKind: "number",
                op: "disable",
            });
        }
        else {
            appendSetOverrideEntry(entries, {
                fieldPath: maxHoursPath,
                path: "aiRunLimits.maxHours",
                expectedKind: "number",
                value: overrides.aiRunLimits.maxHours,
            });
        }
    }
    appendSetOverrideEntry(entries, {
        fieldPath: ["workflow", "overrides", "recurring", "maxActiveChildren"],
        path: "recurring.maxActiveChildren",
        expectedKind: "integer",
        value: overrides.recurring?.maxActiveChildren,
    });
    appendSetOverrideEntry(entries, {
        fieldPath: ["workflow", "overrides", "followup", "proposalPolicy"],
        path: "followup.proposalPolicy",
        expectedKind: "enum",
        value: overrides.followup?.proposalPolicy,
    });
    appendSetOverrideEntry(entries, {
        fieldPath: ["workflow", "overrides", "followup", "proposalApproval"],
        path: "followup.proposalApproval",
        expectedKind: "enum",
        value: overrides.followup?.proposalApproval,
    });
    appendSetOverrideEntry(entries, {
        fieldPath: [
            "workflow",
            "overrides",
            "followup",
            "maxIssuesPerSource",
            "mainWork",
        ],
        path: "followup.maxIssuesPerSource.mainWork",
        expectedKind: "integer",
        value: overrides.followup?.maxIssuesPerSource?.mainWork,
    });
    appendSetOverrideEntry(entries, {
        fieldPath: [
            "workflow",
            "overrides",
            "followup",
            "maxIssuesPerSource",
            "maintenance",
        ],
        path: "followup.maxIssuesPerSource.maintenance",
        expectedKind: "integer",
        value: overrides.followup?.maxIssuesPerSource?.maintenance,
    });
    appendSetOverrideEntry(entries, {
        fieldPath: ["workflow", "overrides", "defaultReviewers"],
        path: "repositoryHost.pullRequest.defaultReviewers",
        expectedKind: "string-array",
        value: overrides.defaultReviewers === undefined ||
            overrides.defaultReviewers === null
            ? overrides.defaultReviewers
            : [...new Set(overrides.defaultReviewers)],
    });
    appendSetOverrideEntry(entries, {
        fieldPath: ["workflow", "overrides", "foreignIssueApprovalScope"],
        path: "foreignIssueApprovalScope",
        expectedKind: "enum",
        value: overrides.foreignIssueApprovalScope,
    });
    const workflowStart = overrides.workflowStart;
    if (workflowStart !== undefined && workflowStart !== null) {
        appendSetOverrideEntry(entries, {
            fieldPath: ["workflow", "overrides", "workflowStart", "newPlan"],
            path: "approvals.workflowStart.newPlan",
            expectedKind: "enum",
            value: workflowStart.newPlan,
        });
        appendSetOverrideEntry(entries, {
            fieldPath: ["workflow", "overrides", "workflowStart", "replan"],
            path: "approvals.workflowStart.replan",
            expectedKind: "enum",
            value: workflowStart.replan,
        });
        appendSetOverrideEntry(entries, {
            fieldPath: ["workflow", "overrides", "workflowStart", "scope"],
            path: "approvals.workflowStart.scope",
            expectedKind: "enum",
            value: workflowStart.scope,
        });
    }
    if (entries.length === 0) {
        return null;
    }
    return { entries };
}
function validateStructuredOverrides(overrides, issues) {
    const defaultSentinelPath = findDefaultSentinelPath(overrides, [
        "workflow",
        "overrides",
    ]);
    if (defaultSentinelPath !== null) {
        (0, validation_issues_1.addWorkerSemanticValidationIssue)(issues, {
            path: defaultSentinelPath,
            message: "workflow.overrides must not use the config inherit sentinel; omit the field or use null for no override.",
        });
        return;
    }
    try {
        buildWorkerWorkflowConfigOverridePatch(overrides);
    }
    catch (error) {
        (0, validation_issues_1.addWorkerSemanticValidationIssue)(issues, {
            path: ["workflow", "overrides"],
            message: error instanceof Error
                ? error.message
                : "workflow.overrides must convert to a registry-backed config override patch.",
        });
    }
}
function appendIssueCompletionOverrides(entries, value) {
    if (value === undefined || value === null) {
        return;
    }
    appendSetOverrideEntry(entries, {
        fieldPath: ["workflow", "overrides", "issueCompletion", "target"],
        path: "issueCompletion.target",
        expectedKind: "enum",
        value: value.target,
    });
    appendSetOverrideEntry(entries, {
        fieldPath: [
            "workflow",
            "overrides",
            "issueCompletion",
            "pullRequest",
            "reviewPolicy",
        ],
        path: "issueCompletion.pullRequest.reviewPolicy",
        expectedKind: "enum",
        value: value.pullRequest?.reviewPolicy,
    });
    appendSetOverrideEntry(entries, {
        fieldPath: [
            "workflow",
            "overrides",
            "issueCompletion",
            "pullRequest",
            "mergePolicy",
        ],
        path: "issueCompletion.pullRequest.mergePolicy",
        expectedKind: "enum",
        value: value.pullRequest?.mergePolicy,
    });
}
function appendSetOverrideEntry(entries, input) {
    if (input.value === undefined || input.value === null) {
        return;
    }
    const row = requireRegistryRow(input.path, input.expectedKind, input.fieldPath);
    if (row.kind === "enum" &&
        (typeof input.value !== "string" || !row.enumValues?.includes(input.value))) {
        throw new Error(`${formatIssuePath(input.fieldPath)} is not a supported enum value.`);
    }
    entries.push({
        path: row.path,
        op: "set",
        value: input.value,
    });
}
function appendOperationOverrideEntry(entries, input) {
    const row = requireRegistryRow(input.path, input.expectedKind, input.fieldPath);
    entries.push({
        path: row.path,
        op: input.op,
    });
}
function requireRegistryRow(path, expectedKind, fieldPath) {
    const row = issue_override_option_registry_1.ISSUE_OVERRIDE_OPTION_REGISTRY_BY_PATH.get(path);
    if (row === undefined) {
        throw new Error(`${formatIssuePath(fieldPath)} is not backed by the override registry.`);
    }
    if (row.kind !== expectedKind) {
        throw new Error(`${formatIssuePath(fieldPath)} does not match the override registry type.`);
    }
    return row;
}
function findDefaultSentinelPath(value, issuePath) {
    if (value === types_1.CONFIG_OVERRIDE_INHERIT_SENTINEL) {
        return issuePath;
    }
    if (Array.isArray(value)) {
        for (const [index, entry] of value.entries()) {
            const found = findDefaultSentinelPath(entry, [...issuePath, index]);
            if (found !== null) {
                return found;
            }
        }
        return null;
    }
    if (typeof value === "object" && value !== null) {
        for (const [key, entry] of Object.entries(value)) {
            const found = findDefaultSentinelPath(entry, [...issuePath, key]);
            if (found !== null) {
                return found;
            }
        }
    }
    return null;
}
function formatIssuePath(path) {
    return path.join(".");
}
