"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadHooks = loadHooks;
exports.createHookVM = createHookVM;
exports.executeHook = executeHook;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const logger_1 = require("./logger");
// ─── Loader ──────────────────────────────────────────────────────────────────
/**
 * Loads hooks from the file path specified by AIDEV_HOOKS_PATH env var or
 * the config's hooksPath. Supports .ts (via jiti) and .js files.
 * Returns an empty object if no hooks file is configured or found. Exports that are not
 * known hook names are stripped so the returned object only contains hook functions.
 */
function loadHooks(hooksPath) {
    if (!hooksPath)
        return {};
    const resolved = path.isAbsolute(hooksPath)
        ? hooksPath
        : path.resolve(process.cwd(), hooksPath);
    if (!fs.existsSync(resolved)) {
        logger_1.logger.warn(`Hooks file not found: ${resolved} — hooks disabled`);
        return {};
    }
    try {
        let mod;
        if (resolved.endsWith('.ts')) {
            // Use jiti to load TypeScript files at runtime — no compiler needed
            // eslint-disable-next-line @typescript-eslint/no-var-requires
            const { createJiti } = require('jiti');
            const jiti = createJiti(__filename, { interopDefault: true });
            mod = jiti(resolved);
        }
        else {
            // eslint-disable-next-line @typescript-eslint/no-var-requires
            mod = require(resolved);
        }
        const hooks = (mod.default || mod);
        const validKeys = new Set([
            'beforeRun', 'afterRun',
            'beforeEachTask', 'afterEachTask',
            'beforeResolveConflicts', 'afterResolveConflicts',
            'beforeNonCodeTask', 'afterNonCodeTask',
            'beforeThinkingTask', 'afterThinkingTask',
            'beforeReviewTask', 'afterReviewTask',
        ]);
        // Keep only known hook names; drop unknown keys and non-functions
        for (const key of Object.keys(hooks)) {
            if (!validKeys.has(key)) {
                delete hooks[key];
                continue;
            }
            const val = hooks[key];
            if (typeof val !== 'function') {
                logger_1.logger.warn(`Hook "${key}" is not a function — ignoring`);
                delete hooks[key];
            }
        }
        const loaded = Object.keys(hooks).filter((k) => validKeys.has(k) && typeof hooks[k] === 'function');
        if (loaded.length > 0) {
            logger_1.logger.info(`Hooks loaded: ${loaded.join(', ')}`);
        }
        else {
            logger_1.logger.debug('Hooks file loaded but no hook functions found');
        }
        return hooks;
    }
    catch (err) {
        logger_1.logger.error(`Failed to load hooks from ${resolved}: ${err}`);
        return {};
    }
}
// ─── Executor ────────────────────────────────────────────────────────────────
function createHookVM(provider, runners) {
    return {
        async runAI(prompt) {
            const runner = runners.find((r) => r.isAvailable());
            if (!runner) {
                return { success: false, output: '', error: 'No AI runner available' };
            }
            return runner.run(prompt);
        },
        async postComment(taskId, text) {
            await provider.postComment(taskId, text);
        },
        async updateStatus(taskId, status) {
            await provider.updateStatus(taskId, status);
        },
        async getComments(taskId) {
            const comments = await provider.getComments(taskId);
            return comments.map((c) => ({ id: c.id, text: c.text, author: c.author }));
        },
        log: {
            info: (msg) => logger_1.logger.info(`[hook] ${msg}`),
            warn: (msg) => logger_1.logger.warn(`[hook] ${msg}`),
            error: (msg) => logger_1.logger.error(`[hook] ${msg}`),
        },
    };
}
/**
 * Safely execute a hook function. If the hook throws, the error propagates
 * to the caller (which should abort the operation). If the hook returns a
 * modified context, that context is returned; otherwise the original is.
 */
async function executeHook(hooks, hookName, context, vm) {
    const fn = hooks[hookName];
    if (!fn)
        return context;
    logger_1.logger.debug(`Executing hook: ${hookName}`);
    const result = await fn(context, vm);
    return (result ?? context);
}
//# sourceMappingURL=hooks.js.map