"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HookRouter = void 0;
const node_crypto_1 = require("node:crypto");
const node_http_1 = require("node:http");
class HookRouter {
    server = null;
    port = 0;
    sessions = new Map();
    async start(host = "127.0.0.1", port = 0) {
        this.server = (0, node_http_1.createServer)((req, res) => this.handle(req, res));
        await new Promise((resolve, reject) => {
            this.server.once("error", reject);
            this.server.listen(port, host, () => {
                const addr = this.server.address();
                if (addr && typeof addr === "object")
                    this.port = addr.port;
                resolve();
            });
        });
        return { host, port: this.port };
    }
    async stop() {
        if (!this.server)
            return;
        await new Promise((resolve) => this.server.close(() => resolve()));
        this.server = null;
    }
    getUrl(host = "127.0.0.1") {
        return `http://${host}:${this.port}`;
    }
    register(sessionId, handler) {
        const token = (0, node_crypto_1.randomBytes)(24).toString("hex");
        this.sessions.set(sessionId, { token, handler, pendingResponses: new Set() });
        return token;
    }
    unregister(sessionId) {
        const registered = this.sessions.get(sessionId);
        if (registered) {
            for (const res of registered.pendingResponses) {
                if (!res.writableEnded) {
                    res.statusCode = 503;
                    res.setHeader("Content-Type", "application/json");
                    res.end(JSON.stringify({ exitCode: 0, stderr: "session restarted" }));
                }
            }
            registered.pendingResponses.clear();
        }
        this.sessions.delete(sessionId);
    }
    async handle(req, res) {
        if (req.method !== "POST") {
            res.statusCode = 405;
            res.end();
            return;
        }
        const url = req.url || "";
        const match = url.match(/^\/hook\/([A-Za-z]+)(?:\?.*)?$/);
        if (!match) {
            res.statusCode = 404;
            res.end();
            return;
        }
        const eventName = match[1];
        const sessionId = req.headers["x-cockpit-session"];
        const token = req.headers["x-cockpit-token"];
        if (typeof sessionId !== "string" || typeof token !== "string") {
            res.statusCode = 400;
            res.end("missing session or token header");
            return;
        }
        const registered = this.sessions.get(sessionId);
        if (!registered || registered.token !== token) {
            res.statusCode = 403;
            res.end("unauthorized");
            return;
        }
        let body = "";
        req.setEncoding("utf8");
        for await (const chunk of req)
            body += chunk;
        let payload;
        try {
            payload = body ? JSON.parse(body) : {};
        }
        catch {
            res.statusCode = 400;
            res.end("invalid json");
            return;
        }
        console.log(`[hook-router] received ${eventName} for session ${sessionId.slice(0, 8)}`);
        registered.pendingResponses.add(res);
        try {
            const response = await this.dispatch(registered.handler, eventName, payload);
            if (!res.writableEnded) {
                res.statusCode = 200;
                res.setHeader("Content-Type", "application/json");
                res.end(JSON.stringify(response));
            }
            console.log(`[hook-router] dispatched ${eventName} for session ${sessionId.slice(0, 8)} ok`);
        }
        catch (err) {
            console.error(`[hook-router] dispatch ${eventName} for session ${sessionId.slice(0, 8)} THREW:`, err);
            if (!res.writableEnded) {
                res.statusCode = 500;
                res.setHeader("Content-Type", "application/json");
                res.end(JSON.stringify({ exitCode: 1, stderr: String(err) }));
            }
        }
        finally {
            registered.pendingResponses.delete(res);
        }
    }
    async dispatch(handler, eventName, payload) {
        if (eventName === "PermissionRequest") {
            if (!handler.onPermissionRequest) {
                return { stdout: permissionAllowJson(), exitCode: 0 };
            }
            const decision = await handler.onPermissionRequest(payload);
            return { stdout: permissionDecisionJson(decision), exitCode: 0 };
        }
        const handlerMap = {
            PreToolUse: handler.onPreToolUse,
            PostToolUse: handler.onPostToolUse,
            Stop: handler.onStop,
            StopFailure: handler.onStopFailure,
            UserPromptSubmit: handler.onUserPromptSubmit,
            UserPromptExpansion: handler.onUserPromptExpansion,
            SubagentStart: handler.onSubagentStart,
            SubagentStop: handler.onSubagentStop,
            Notification: handler.onNotification,
            PreCompact: handler.onPreCompact,
            PostCompact: handler.onPostCompact,
        };
        const fn = handlerMap[eventName];
        if (!fn) {
            console.log(`[hook-router] no handler for ${eventName}, returning empty response`);
            return {};
        }
        const result = await fn(payload);
        return result || {};
    }
}
exports.HookRouter = HookRouter;
function permissionAllowJson() {
    return JSON.stringify({ hookSpecificOutput: { hookEventName: "PermissionRequest", decision: { behavior: "allow" } } });
}
function permissionDecisionJson(decision) {
    return JSON.stringify({ hookSpecificOutput: { hookEventName: "PermissionRequest", decision } });
}
