"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.inferEventHookRunner = inferEventHookRunner;
exports.getDefaultEventHookExtensionForRunner = getDefaultEventHookExtensionForRunner;
exports.createRepoEventHookConfigSchema = createRepoEventHookConfigSchema;
exports.parseUserEventHookConfigFile = parseUserEventHookConfigFile;
exports.validateRepoEventHooks = validateRepoEventHooks;
exports.validateUserEventHooks = validateUserEventHooks;
exports.parseEventHookTrigger = parseEventHookTrigger;
exports.parseEventHookTriggerList = parseEventHookTriggerList;
exports.normalizeEventHookConfigPath = normalizeEventHookConfigPath;
exports.isWorkflowLabelTrigger = isWorkflowLabelTrigger;
const node_path_1 = __importDefault(require("node:path"));
const zod_1 = require("zod");
const catalog_1 = require("../i18n/catalog");
const workflow_labels_1 = require("../platform/ticket-tracker/workflow-labels");
const types_1 = require("./types");
const EVENT_HOOK_EXTENSION_RUNNERS = {
    ".md": "worker",
    ".js": "node",
    ".mjs": "node",
    ".cjs": "node",
    ".py": "python3",
    ".sh": "bash",
};
const EVENT_HOOK_SUPPORTED_EXTENSIONS = Object.keys(EVENT_HOOK_EXTENSION_RUNNERS);
const EVENT_HOOK_TRIGGER_VALUES = [
    ...workflow_labels_1.WORKFLOW_LABEL_KEYS,
    ...types_1.EVENT_HOOK_BUILTIN_TRIGGERS,
];
function inferEventHookRunner(hookPath) {
    const extension = node_path_1.default.extname(hookPath);
    const runner = EVENT_HOOK_EXTENSION_RUNNERS[extension];
    if (runner === undefined) {
        throw new Error(`Unsupported event hook extension: ${extension}`);
    }
    return runner;
}
function getDefaultEventHookExtensionForRunner(runner) {
    switch (runner) {
        case "worker":
            return ".md";
        case "node":
            return ".mjs";
        case "python3":
            return ".py";
        case "bash":
            return ".sh";
        default:
            return null;
    }
}
function createRepoEventHookConfigSchema(locale = catalog_1.DEFAULT_APP_LOCALE) {
    void locale;
    return createEventHookConfigArraySchema({ allowAbsolutePath: false });
}
function parseUserEventHookConfigFile(value) {
    return zod_1.z
        .object({
        eventHooks: createEventHookConfigArraySchema({
            allowAbsolutePath: false,
        }).default([]),
    })
        .strict()
        .parse(value);
}
function validateRepoEventHooks(hooks) {
    return createEventHookConfigArraySchema({ allowAbsolutePath: false }).parse(hooks);
}
function validateUserEventHooks(hooks) {
    return createEventHookConfigArraySchema({ allowAbsolutePath: false }).parse(hooks);
}
function parseEventHookTrigger(value) {
    if (EVENT_HOOK_TRIGGER_VALUES.includes(value)) {
        return value;
    }
    throw new Error(`Unknown event hook trigger: ${value}`);
}
function parseEventHookTriggerList(values) {
    const parsed = values
        .flatMap((value) => value.split(","))
        .map((value) => value.trim())
        .filter((value) => value.length > 0)
        .map(parseEventHookTrigger);
    if (parsed.length === 0) {
        throw new Error("event hook requires at least one trigger.");
    }
    return [...new Set(parsed)];
}
function normalizeEventHookConfigPath(input) {
    if (node_path_1.default.isAbsolute(input.hookPath)) {
        if (!input.allowAbsolutePath) {
            throw new Error("Event hook path must be relative.");
        }
        return {
            absolutePath: node_path_1.default.normalize(input.hookPath),
            normalizedPath: node_path_1.default.normalize(input.hookPath),
        };
    }
    assertRepositoryRelativeHookPath(input.hookPath);
    const baseDir = node_path_1.default.resolve(input.baseDir);
    const absolutePath = node_path_1.default.resolve(baseDir, input.hookPath);
    const relativePath = node_path_1.default.relative(baseDir, absolutePath);
    if (relativePath === "" ||
        relativePath.startsWith("..") ||
        node_path_1.default.isAbsolute(relativePath)) {
        throw new Error("Event hook path must stay inside its base directory.");
    }
    return {
        absolutePath,
        normalizedPath: normalizeRepositoryRelativePath(relativePath),
    };
}
function createEventHookConfigArraySchema(input) {
    return zod_1.z
        .array(createEventHookConfigSchema(input))
        .default([])
        .superRefine((hooks, context) => {
        const seen = new Set();
        for (const [index, hook] of hooks.entries()) {
            const normalized = normalizePathForDuplicateCheck(hook.path);
            if (seen.has(normalized)) {
                context.addIssue({
                    code: "custom",
                    message: `Duplicate event hook path: ${hook.path}`,
                    path: [index, "path"],
                });
            }
            seen.add(normalized);
        }
    });
}
function createEventHookConfigSchema(input) {
    return zod_1.z
        .object({
        path: zod_1.z
            .string()
            .min(1)
            .refine((value) => node_path_1.default.isAbsolute(value)
            ? input.allowAbsolutePath
            : isRepositoryRelativeHookPath(value), { message: "Invalid event hook path." })
            .refine((value) => isSupportedEventHookPath(value), {
            message: "Unsupported event hook extension.",
        }),
        runner: zod_1.z.string().min(1).optional(),
        on: zod_1.z.array(zod_1.z.enum(EVENT_HOOK_TRIGGER_VALUES)).min(1),
    })
        .strict();
}
function isSupportedEventHookPath(value) {
    return EVENT_HOOK_SUPPORTED_EXTENSIONS.includes(node_path_1.default.extname(value));
}
function isRepositoryRelativeHookPath(value) {
    try {
        assertRepositoryRelativeHookPath(value);
        return true;
    }
    catch {
        return false;
    }
}
function assertRepositoryRelativeHookPath(value) {
    if (value.trim() !== value || value.length === 0) {
        throw new Error("Event hook path must be non-empty and trimmed.");
    }
    if (node_path_1.default.isAbsolute(value)) {
        throw new Error("Event hook path must be repository-relative.");
    }
    const normalized = node_path_1.default.normalize(value);
    if (normalized === "." ||
        normalized.startsWith("..") ||
        node_path_1.default.isAbsolute(normalized)) {
        throw new Error("Event hook path must stay inside the repository.");
    }
}
function normalizePathForDuplicateCheck(value) {
    return node_path_1.default.isAbsolute(value)
        ? node_path_1.default.normalize(value)
        : normalizeRepositoryRelativePath(value);
}
function normalizeRepositoryRelativePath(value) {
    return node_path_1.default.normalize(value).replaceAll(node_path_1.default.sep, "/");
}
function isWorkflowLabelTrigger(trigger) {
    return workflow_labels_1.WORKFLOW_LABEL_KEYS.includes(trigger);
}
