import fs from "fs-extra";
import * as path from "path";
import os from "os";
import { spawn } from "child_process";
import { getErrorMessage } from "../types/index.js";
import { logger } from "../utils/logger.js";
// Default hooks configuration - available for initialization
const _DEFAULT_HOOKS_CONFIG = {
    hooks: []
};
export class HookManager {
    hooks = [];
    configPath;
    globalConfigPath;
    enabled = true;
    constructor() {
        this.configPath = path.join(process.cwd(), ".codebuddy", "hooks.json");
        this.globalConfigPath = path.join(process.env.HOME || process.env.USERPROFILE || os.homedir(), ".codebuddy", "hooks.json");
        this.loadHooks();
    }
    loadHooks() {
        // Load global hooks first
        if (fs.existsSync(this.globalConfigPath)) {
            try {
                const globalConfig = fs.readJsonSync(this.globalConfigPath);
                this.hooks = [...(globalConfig.hooks || [])];
            }
            catch (error) {
                logger.warn(`Failed to load global hooks config: ${error instanceof Error ? error.message : String(error)}`);
            }
        }
        // Load project-level hooks (override/extend global)
        if (fs.existsSync(this.configPath)) {
            try {
                const projectConfig = fs.readJsonSync(this.configPath);
                this.hooks = [...this.hooks, ...(projectConfig.hooks || [])];
            }
            catch (error) {
                logger.warn(`Failed to load project hooks config: ${error instanceof Error ? error.message : String(error)}`);
            }
        }
    }
    reloadHooks() {
        this.hooks = [];
        this.loadHooks();
    }
    setEnabled(enabled) {
        this.enabled = enabled;
    }
    isEnabled() {
        return this.enabled;
    }
    getHooks() {
        return [...this.hooks];
    }
    getHooksForEvent(event) {
        return this.hooks.filter((hook) => hook.event === event && hook.enabled !== false);
    }
    addHook(hook) {
        this.hooks.push(hook);
        this.saveHooks();
    }
    removeHook(index) {
        if (index >= 0 && index < this.hooks.length) {
            this.hooks.splice(index, 1);
            this.saveHooks();
            return true;
        }
        return false;
    }
    saveHooks() {
        try {
            const dir = path.dirname(this.configPath);
            fs.ensureDirSync(dir);
            fs.writeJsonSync(this.configPath, { hooks: this.hooks }, { spaces: 2 });
        }
        catch (error) {
            logger.warn(`Failed to save hooks config: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async executeHooks(event, context) {
        if (!this.enabled) {
            return { success: true };
        }
        const fullContext = {
            event,
            timestamp: new Date(),
            ...context,
        };
        const hooks = this.getHooksForEvent(event);
        if (hooks.length === 0) {
            return { success: true };
        }
        let combinedResult = { success: true };
        for (const hook of hooks) {
            // Check pattern matching for tool-specific hooks
            if (hook.pattern && fullContext.toolName) {
                const regex = new RegExp(hook.pattern);
                if (!regex.test(fullContext.toolName)) {
                    continue;
                }
            }
            try {
                const result = await this.executeHook(hook, fullContext);
                if (!result.success) {
                    combinedResult.success = false;
                    combinedResult.error = result.error;
                }
                if (result.blocked) {
                    combinedResult.blocked = true;
                    combinedResult.error = result.error || "Operation blocked by hook";
                    break; // Stop processing further hooks
                }
                if (result.modifiedArgs) {
                    combinedResult.modifiedArgs = {
                        ...combinedResult.modifiedArgs,
                        ...result.modifiedArgs,
                    };
                }
                if (result.output) {
                    combinedResult.output = combinedResult.output
                        ? `${combinedResult.output}\n${result.output}`
                        : result.output;
                }
            }
            catch (error) {
                logger.warn(`Hook execution error: ${getErrorMessage(error)}`);
                // Continue with other hooks even if one fails
            }
        }
        return combinedResult;
    }
    async executeHook(hook, context) {
        return new Promise((resolve) => {
            const timeout = hook.timeout || 30000;
            const child = spawn("sh", ["-c", hook.command], {
                stdio: ["pipe", "pipe", "pipe"],
                env: {
                    ...process.env,
                    GROK_HOOK_EVENT: context.event,
                    GROK_HOOK_TOOL: context.toolName || "",
                    GROK_HOOK_SESSION: context.sessionId || "",
                },
            });
            let stdout = "";
            let stderr = "";
            let timedOut = false;
            const timer = setTimeout(() => {
                timedOut = true;
                child.kill("SIGTERM");
            }, timeout);
            // Send context as JSON to stdin
            child.stdin.write(JSON.stringify(context));
            child.stdin.end();
            child.stdout.on("data", (data) => {
                stdout += data.toString();
            });
            child.stderr.on("data", (data) => {
                stderr += data.toString();
            });
            child.on("close", (code) => {
                clearTimeout(timer);
                if (timedOut) {
                    resolve({
                        success: false,
                        error: `Hook timed out after ${timeout}ms`,
                    });
                    return;
                }
                // Try to parse stdout as JSON for advanced hook responses
                let parsedOutput = null;
                try {
                    parsedOutput = JSON.parse(stdout.trim());
                }
                catch {
                    // Not JSON, use as plain text
                }
                if (code === 0) {
                    resolve({
                        success: true,
                        output: stdout.trim(),
                        blocked: parsedOutput?.blocked,
                        modifiedArgs: parsedOutput?.modifiedArgs,
                    });
                }
                else if (code === 77) {
                    // Special exit code to block the operation
                    resolve({
                        success: false,
                        blocked: true,
                        error: stderr.trim() || stdout.trim() || "Operation blocked by hook",
                    });
                }
                else {
                    resolve({
                        success: false,
                        error: stderr.trim() || `Hook exited with code ${code}`,
                    });
                }
            });
            child.on("error", (error) => {
                clearTimeout(timer);
                resolve({
                    success: false,
                    error: `Hook execution failed: ${error.message}`,
                });
            });
        });
    }
    formatStatus() {
        const status = this.enabled ? "enabled" : "disabled";
        const hookCount = this.hooks.length;
        let output = `Hook System: ${status}\n`;
        output += `Total hooks: ${hookCount}\n\n`;
        if (hookCount > 0) {
            output += "Configured Hooks:\n";
            this.hooks.forEach((hook, index) => {
                const enabledStatus = hook.enabled !== false ? "✓" : "✗";
                output += `  ${index + 1}. [${enabledStatus}] ${hook.event}`;
                if (hook.pattern) {
                    output += ` (pattern: ${hook.pattern})`;
                }
                output += `\n     Command: ${hook.command}\n`;
            });
        }
        return output;
    }
}
// Singleton instance
let hookManagerInstance = null;
export function getHookManager() {
    if (!hookManagerInstance) {
        hookManagerInstance = new HookManager();
    }
    return hookManagerInstance;
}
//# sourceMappingURL=hook-manager.js.map