"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runEventHookCommand = runEventHookCommand;
const promises_1 = require("node:fs/promises");
const node_path_1 = __importDefault(require("node:path"));
const control_plane_config_1 = require("../config/control-plane-config");
const global_settings_1 = require("../core/global-settings");
const repo_registry_1 = require("../core/repo-registry");
const config_1 = require("../event-hooks/config");
const path_1 = require("../event-hooks/path");
const runner_1 = require("../event-hooks/runner");
const sample_payload_1 = require("../event-hooks/sample-payload");
const storage_1 = require("../event-hooks/storage");
const catalog_1 = require("../i18n/catalog");
const event_hook_create_1 = require("./event-hook-create");
const json_output_1 = require("./json-output");
async function runEventHookCommand(cwd, args) {
    switch (args.action) {
        case "add":
            return runEventHookAddCommand(cwd, args);
        case "list":
            return runEventHookListCommand(cwd, args);
        case "remove":
            return runEventHookRemoveCommand(cwd, args);
        case "test":
            return runEventHookTestCommand(cwd, args);
        case "config":
            return runEventHookConfigCommand(cwd, args);
        case "create":
            return (0, event_hook_create_1.runEventHookCreateCommand)(cwd, args);
        case "update":
            return (0, event_hook_create_1.runEventHookUpdateCommand)(cwd, args);
    }
}
async function runEventHookAddCommand(cwd, args) {
    if (args.scope === "repo") {
        const hook = normalizeHookInput(args);
        const targetCwd = resolveSingleTargetCwd(cwd, args.cwd);
        const config = await (0, control_plane_config_1.loadConfig)(targetCwd);
        const nextHooks = (0, config_1.validateRepoEventHooks)([...config.eventHooks, hook]);
        const nextConfig = { ...config, eventHooks: nextHooks };
        await (0, promises_1.writeFile)((0, control_plane_config_1.getConfigPath)(targetCwd), (0, control_plane_config_1.formatConfigFile)(nextConfig), "utf8");
        console.log(renderHookAdded(config.i18n.defaultLanguage, "repo", hook.path));
        return 0;
    }
    (0, storage_1.requireUserEventHookDirectory)();
    const hook = normalizeHookInput(args);
    const target = await resolveUserStorageTarget(cwd, args);
    await assertUserHookFileExists(hook.path);
    const current = await (0, storage_1.loadUserEventHooks)({ target });
    const nextHooks = (0, config_1.validateUserEventHooks)([...current, hook]);
    const filePath = await (0, storage_1.writeUserEventHooks)({
        target,
        eventHooks: nextHooks,
    });
    console.log(renderHookAdded(targetLocale(target), renderUserScope(target), hook.path));
    console.log(filePath);
    return 0;
}
async function runEventHookListCommand(cwd, args) {
    const { entries, locale } = await loadEventHookListEntries(cwd, args);
    if (entries.length === 0) {
        console.log((0, catalog_1.translate)(locale, "eventHook.command.list.empty"));
        return 0;
    }
    console.log("repository\tscope\tpath\trunner\ton");
    for (const entry of entries) {
        console.log([
            entry.repository,
            entry.scope,
            entry.hook.path,
            entry.hook.runner ?? (0, config_1.inferEventHookRunner)(entry.hook.path),
            entry.hook.on.join(","),
        ].join("\t"));
    }
    return 0;
}
async function loadEventHookListEntries(cwd, args) {
    const targetCwds = args.allRepos
        ? await loadRegisteredRepoCwds()
        : [resolveSingleTargetCwd(cwd, args.cwd)];
    const entries = [];
    let locale = catalog_1.DEFAULT_APP_LOCALE;
    for (const targetCwd of targetCwds) {
        const config = await (0, control_plane_config_1.loadConfig)(targetCwd);
        locale = config.i18n.defaultLanguage;
        const repository = formatRepositoryLabel(config);
        if (args.scope === "repo") {
            entries.push(...config.eventHooks.map((hook) => ({
                repository,
                scope: "repo",
                hook,
            })));
            continue;
        }
        if (args.scope === "user") {
            const userHooks = await (0, storage_1.loadUserEventHookEntries)({ config });
            entries.push(...userHooks.map((entry) => ({
                repository,
                scope: entry.source,
                hook: entry.hook,
            })));
            continue;
        }
        entries.push(...(await (0, runner_1.loadResolvedEventHooks)({ repoRoot: targetCwd, config })).map((hook) => ({
            repository,
            scope: hook.source,
            hook,
        })));
    }
    return { entries, locale };
}
async function runEventHookRemoveCommand(cwd, args) {
    if (args.scope === "repo") {
        const targetCwd = resolveSingleTargetCwd(cwd, args.cwd);
        const config = await (0, control_plane_config_1.loadConfig)(targetCwd);
        const nextHooks = config.eventHooks.filter((hook) => !(0, path_1.isMatchingEventHookPath)({
            baseDir: targetCwd,
            allowAbsolutePath: false,
            configuredPath: hook.path,
            requestedPath: args.path,
        }));
        if (nextHooks.length === config.eventHooks.length) {
            console.error(renderHookNotFound(config.i18n.defaultLanguage, "repo", args.path));
            return 1;
        }
        const nextConfig = { ...config, eventHooks: nextHooks };
        await (0, promises_1.writeFile)((0, control_plane_config_1.getConfigPath)(targetCwd), (0, control_plane_config_1.formatConfigFile)(nextConfig), "utf8");
        console.log(renderHookRemoved(config.i18n.defaultLanguage, "repo", args.path));
        return 0;
    }
    const userDirectory = (0, storage_1.requireUserEventHookDirectory)();
    const target = await resolveUserStorageTarget(cwd, args);
    const current = await (0, storage_1.loadUserEventHooks)({ target });
    const nextHooks = current.filter((hook) => !(0, path_1.isMatchingEventHookPath)({
        baseDir: userDirectory,
        allowAbsolutePath: false,
        configuredPath: hook.path,
        requestedPath: args.path,
    }));
    if (nextHooks.length === current.length) {
        console.error(renderHookNotFound(targetLocale(target), renderUserScope(target), args.path));
        return 1;
    }
    const filePath = await (0, storage_1.writeUserEventHooks)({
        target,
        eventHooks: nextHooks,
    });
    console.log(renderHookRemoved(targetLocale(target), renderUserScope(target), args.path));
    console.log(filePath);
    return 0;
}
async function runEventHookTestCommand(cwd, args) {
    const config = await (0, control_plane_config_1.loadConfig)(cwd);
    const scope = args.scope ?? "repo";
    const baseDir = scope === "user" ? (0, storage_1.requireUserEventHookDirectory)() : cwd;
    const hook = normalizeHookInput({
        path: args.path,
        ...(args.runner !== undefined ? { runner: args.runner } : {}),
        on: ["completed", "all"],
    });
    console.log((0, catalog_1.translate)(config.i18n.defaultLanguage, "eventHook.command.test"));
    const result = await (0, runner_1.runSingleEventHook)({
        repoRoot: cwd,
        config,
        hook,
        source: scope === "user" ? "user:all" : "repo",
        baseDir,
        allowAbsolutePath: false,
        payload: (0, sample_payload_1.buildSampleEventHookPayload)(hook),
    });
    (0, json_output_1.printCommandJson)(result);
    return result.status === "failed" ? 1 : 0;
}
async function runEventHookConfigCommand(cwd, args) {
    if (args.command === "show") {
        const userDirectory = (0, storage_1.getUserEventHookDirectory)();
        if (userDirectory === null) {
            console.log((0, catalog_1.translate)(catalog_1.DEFAULT_APP_LOCALE, "eventHook.command.config.unset"));
            console.log((0, catalog_1.translate)(catalog_1.DEFAULT_APP_LOCALE, "eventHook.command.config.guidance"));
            return 0;
        }
        console.log((0, catalog_1.translate)(catalog_1.DEFAULT_APP_LOCALE, "eventHook.command.config.current", {
            path: userDirectory,
        }));
        return 0;
    }
    const userDirectory = (0, global_settings_1.normalizeUserEventHookDirectory)(node_path_1.default.resolve(cwd, args.userDir));
    await (0, promises_1.mkdir)(userDirectory, { recursive: true });
    const settings = (0, global_settings_1.loadControlPlaneGlobalSettings)();
    const settingsPath = (0, global_settings_1.writeControlPlaneGlobalSettings)({
        ...settings,
        eventHooks: {
            ...(settings.eventHooks ?? {}),
            userDirectory,
        },
    });
    console.log((0, catalog_1.translate)(catalog_1.DEFAULT_APP_LOCALE, "eventHook.command.config.saved", {
        path: userDirectory,
    }));
    console.log(settingsPath);
    return 0;
}
async function resolveUserStorageTarget(cwd, args) {
    if (args.allRepos) {
        return { scope: "global" };
    }
    const config = await (0, control_plane_config_1.loadConfig)(resolveSingleTargetCwd(cwd, args.cwd));
    return projectUserTarget(config);
}
async function loadRegisteredRepoCwds() {
    const registry = await (0, repo_registry_1.loadRepoRegistry)();
    return registry.repos
        .filter((entry) => entry.enabled)
        .map((entry) => entry.cwd)
        .sort((left, right) => left.localeCompare(right));
}
function resolveSingleTargetCwd(commandCwd, cwdValue) {
    return cwdValue === undefined
        ? commandCwd
        : node_path_1.default.resolve(commandCwd, cwdValue);
}
function projectUserTarget(config) {
    return { scope: "project", config };
}
function renderUserScope(target) {
    return target.scope === "global" ? "user:all" : "user:project";
}
function targetLocale(target) {
    return target.scope === "project"
        ? target.config.i18n.defaultLanguage
        : catalog_1.DEFAULT_APP_LOCALE;
}
async function assertUserHookFileExists(hookPath) {
    const resolved = (0, storage_1.resolveUserEventHookFilePath)({ hookPath });
    let fileStat;
    try {
        fileStat = await (0, promises_1.stat)(resolved.absolutePath);
    }
    catch (error) {
        if (isMissingFileError(error)) {
            throw new Error(`User event hook file not found: ${resolved.absolutePath}`);
        }
        throw error;
    }
    if (!fileStat.isFile()) {
        throw new Error(`User event hook path is not a file: ${resolved.absolutePath}`);
    }
}
function isMissingFileError(error) {
    return (typeof error === "object" &&
        error !== null &&
        "code" in error &&
        error.code === "ENOENT");
}
function renderHookAdded(locale, scope, hookPath) {
    return (0, catalog_1.translate)(locale, "eventHook.command.added", {
        scope,
        path: hookPath,
    });
}
function renderHookRemoved(locale, scope, hookPath) {
    return (0, catalog_1.translate)(locale, "eventHook.command.removed", {
        scope,
        path: hookPath,
    });
}
function renderHookNotFound(locale, scope, hookPath) {
    return (0, catalog_1.translate)(locale, "eventHook.command.notFound", {
        scope,
        path: hookPath,
    });
}
function normalizeHookInput(args) {
    return {
        path: args.path,
        ...(args.runner !== undefined ? { runner: args.runner } : {}),
        on: (0, config_1.parseEventHookTriggerList)(args.on),
    };
}
function formatRepositoryLabel(config) {
    return `${config.repositoryHost.kind}:${config.repositoryHost.projectRef}`;
}
