/**
 * SocketClient - Client for attaching to a draht socket session
 *
 * Connects to a Unix domain socket, sends input, and receives output.
 * Used when running `draht --attach <session-id>`.
 */
import { connect } from "node:net";
/**
 * SocketClient connects to a socket-based draht session.
 *
 * Provides callbacks for output, metadata, and connection events.
 */
export class SocketClient {
    #socketPath;
    #clientId;
    #mode;
    #socket = null;
    #buffer = "";
    /** Callbacks */
    #onOutput = null;
    #onMetadata = null;
    #onClientJoined = null;
    #onClientLeft = null;
    #onInputEcho = null;
    #onError = null;
    #onDisconnect = null;
    constructor(options) {
        this.#socketPath = options.socketPath;
        this.#clientId = options.clientId ?? `client-${crypto.randomUUID().slice(0, 8)}`;
        this.#mode = options.mode ?? "read-write";
    }
    /**
     * Connect to the socket server.
     */
    async connect() {
        return new Promise((resolve, reject) => {
            this.#socket = connect(this.#socketPath);
            this.#socket.on("connect", () => {
                // Send attach message
                const attach = {
                    type: "attach",
                    clientId: this.#clientId,
                    mode: this.#mode,
                };
                this.#send(attach);
                resolve();
            });
            this.#socket.on("data", (data) => {
                this.#handleData(data);
            });
            this.#socket.on("close", () => {
                if (this.#onDisconnect) {
                    this.#onDisconnect();
                }
            });
            this.#socket.on("error", (err) => {
                reject(err);
            });
        });
    }
    /**
     * Disconnect from the socket server.
     */
    disconnect() {
        if (this.#socket) {
            const detach = {
                type: "detach",
                clientId: this.#clientId,
            };
            this.#send(detach);
            this.#socket.end();
            this.#socket = null;
        }
    }
    /**
     * Send input to the session.
     */
    sendInput(data) {
        if (!this.#socket) {
            throw new Error("Not connected");
        }
        const input = {
            type: "input",
            data,
            clientId: this.#clientId,
        };
        this.#send(input);
    }
    /**
     * Set callback for output received from session.
     */
    onOutput(callback) {
        this.#onOutput = callback;
    }
    /**
     * Set callback for session metadata (received on attach).
     */
    onMetadata(callback) {
        this.#onMetadata = callback;
    }
    /**
     * Set callback for when another client joins.
     */
    onClientJoined(callback) {
        this.#onClientJoined = callback;
    }
    /**
     * Set callback for when another client leaves.
     */
    onClientLeft(callback) {
        this.#onClientLeft = callback;
    }
    /**
     * Set callback for input echo from other clients.
     */
    onInputEcho(callback) {
        this.#onInputEcho = callback;
    }
    /**
     * Set callback for errors.
     */
    onError(callback) {
        this.#onError = callback;
    }
    /**
     * Set callback for disconnect.
     */
    onDisconnect(callback) {
        this.#onDisconnect = callback;
    }
    /**
     * Handle incoming data from server.
     */
    #handleData(data) {
        this.#buffer += data.toString();
        const lines = this.#buffer.split("\n");
        this.#buffer = lines.pop() || "";
        for (const line of lines) {
            if (!line.trim())
                continue;
            try {
                const message = JSON.parse(line);
                this.#handleServerMessage(message);
            }
            catch {
                // Ignore malformed messages
            }
        }
    }
    /**
     * Handle a server message.
     */
    #handleServerMessage(message) {
        switch (message.type) {
            case "output":
                if (this.#onOutput) {
                    this.#onOutput(message.data, message.stream);
                }
                break;
            case "input_echo":
                if (this.#onInputEcho) {
                    this.#onInputEcho(message.data, message.clientId);
                }
                break;
            case "client_joined":
                if (this.#onClientJoined) {
                    this.#onClientJoined(message.clientId, message.mode);
                }
                break;
            case "client_left":
                if (this.#onClientLeft) {
                    this.#onClientLeft(message.clientId);
                }
                break;
            case "session_metadata":
                if (this.#onMetadata) {
                    this.#onMetadata(message.sessionId, message.cwd, new Date(message.createdAt));
                }
                break;
            case "error":
                if (this.#onError) {
                    this.#onError(message.message);
                }
                break;
        }
    }
    /**
     * Send a message to the server.
     */
    #send(message) {
        if (!this.#socket)
            return;
        const json = `${JSON.stringify(message)}\n`;
        this.#socket.write(json);
    }
}
//# sourceMappingURL=socket-client.js.map