import type { PermissionReply } from "@dispatch/core";
import { createBunWebSocket } from "hono/bun";
import { agentManager, app, permissionManager } from "./app.js";

const { upgradeWebSocket, websocket } = createBunWebSocket();

let clientIdCounter = 0;

app.get(
	"/ws",
	upgradeWebSocket((_c) => {
		const clientId = String(++clientIdCounter);

		return {
			onOpen(_event, ws) {
				// Send current statuses immediately
				ws.send(JSON.stringify({ type: "statuses", statuses: agentManager.getAllStatuses() }));

				// Send any pending permission prompts
				const pending = permissionManager.getPending();
				if (pending.length > 0) {
					ws.send(JSON.stringify({ type: "permission-prompt", pending }));
				}

				const unsubscribe = agentManager.onEvent((event) => {
					ws.send(JSON.stringify(event));
				});

				permissionManager.registerClient(clientId, (data) => {
					ws.send(JSON.stringify(data));
				});

				// Store cleanup on the raw socket
				(ws as unknown as { _unsub?: () => void; _clientId?: string })._unsub = unsubscribe;
				(ws as unknown as { _unsub?: () => void; _clientId?: string })._clientId = clientId;
			},
			onMessage(event, _ws) {
				try {
					const message = JSON.parse(String(event.data)) as {
						type?: string;
						id?: string;
						reply?: string;
					};
					if (
						message.type === "permission-reply" &&
						typeof message.id === "string" &&
						typeof message.reply === "string"
					) {
						const validReplies: PermissionReply[] = ["once", "always", "reject"];
						if (validReplies.includes(message.reply as PermissionReply)) {
							permissionManager.reply(message.id, message.reply as PermissionReply);
						}
					}
				} catch {
					// ignore malformed messages
				}
			},
			onClose(_event, ws) {
				const raw = ws as unknown as { _unsub?: () => void; _clientId?: string };
				if (raw._unsub) {
					raw._unsub();
				}
				if (raw._clientId) {
					permissionManager.unregisterClient(raw._clientId);
				}
			},
		};
	}),
);

export { app };

export default {
	port: Number(process.env.PORT) || 3000,
	idleTimeout: 60,
	fetch: app.fetch,
	websocket,
};
