import { io } from "socket.io-client";
import { getEndpoint, getBackendProtocol } from "./utils";
import { adaptChunkToLegacy, adaptCompleteToLegacy } from "./response-adapter";
import { Env } from "@stencil/core";
const CONNECTION_TIMEOUT_MS = 30000;
// socket-service.ts
export class SocketService {
    constructor(mode, apiKey = "") {
        this.socketId = "";
        this.socket = null;
        this.mode = Env.DATA_MODE;
        this.connectionPromise = null;
        this.apiKey = "";
        this.protocol = "legacy";
        this.authenticated = false;
        this.mode = mode;
        this.apiKey = apiKey;
        this.protocol = getBackendProtocol(mode);
        this.initSocket();
    }
    //   public static getInstance(mode: string): SocketService {
    //     if (!SocketService.instances.has(mode)) {
    //       SocketService.instances.set(mode, new SocketService(mode))
    //     }
    //     return SocketService.instances.get(mode)!
    //     // return SocketService.instance
    //   }
    initSocket() {
        // console.log("Initializing socket...", this.socket)
        // If a socket already exists, ensure its listeners are removed and it's disconnected
        // before attempting to create a new one. This handles cases where initSocket might
        // be called multiple times or for a reconnection.
        if (this.socket) {
            this.clearSocketListeners(); // Remove all listeners from the existing socket
            this.socket.disconnect(); // Disconnect the socket
            this.socket = null; // Clear the reference to the old socket
        }
        if (!this.socket) {
            let a12z_EndPoint = getEndpoint(this.mode);
            // console.log("a12z_EndPoint", a12z_EndPoint)
            // Create a promise that resolves when the socket connects (and authenticates for v2)
            this.connectionPromise = new Promise((resolve, reject) => {
                let settled = false;
                const timeoutId = setTimeout(() => {
                    if (!settled) {
                        settled = true;
                        console.error("Socket connection timed out");
                        window.dispatchEvent(new CustomEvent("socketError", {
                            detail: { message: "Unable to connect to server. Please try again later.", code: "CONNECTION_TIMEOUT" },
                        }));
                        reject(new Error("Unable to connect to server. Please try again later."));
                    }
                }, CONNECTION_TIMEOUT_MS);
                this.socket = io(a12z_EndPoint, {
                    transports: this.protocol === "v2" ? ["websocket", "polling"] : ["polling"],
                });
                this.socket.on("connect", () => {
                    var _a;
                    // console.log("Socket connected", this.socket?.id)
                    this.socketId = (_a = this.socket) === null || _a === void 0 ? void 0 : _a.id;
                    // Store this instance with its socket ID
                    if (this.socketId) {
                        // console.log("Socket ID:", this.socketId)
                        SocketService.instances.set(this.socketId, this);
                    }
                    if (this.protocol === "v2" && this.apiKey) {
                        // v2: authenticate before resolving
                        this.socket.emit("authenticate", { api_key: this.apiKey });
                    }
                    else {
                        if (!settled) {
                            settled = true;
                            clearTimeout(timeoutId);
                            resolve();
                        }
                    }
                });
                // v2 protocol event listeners
                if (this.protocol === "v2") {
                    this.socket.on("authenticated", () => {
                        this.authenticated = true;
                        if (!settled) {
                            settled = true;
                            clearTimeout(timeoutId);
                            resolve();
                        }
                    });
                    this.socket.on("chunk", (data) => {
                        const adapted = adaptChunkToLegacy(data);
                        window.dispatchEvent(new CustomEvent("streamResponse", { detail: adapted }));
                    });
                    this.socket.on("complete", (data) => {
                        const adapted = adaptCompleteToLegacy(data);
                        window.dispatchEvent(new CustomEvent("endResponse", { detail: adapted }));
                    });
                }
                else {
                    // Legacy protocol event listeners
                    this.socket.on("response", (data) => {
                        // console.log("Received response:", data)
                        window.dispatchEvent(new CustomEvent("streamResponse", { detail: data }));
                    });
                    this.socket.on("end_response", data => {
                        // console.log("Received end_response:", data)
                        window.dispatchEvent(new CustomEvent("endResponse", { detail: data }));
                    });
                }
                this.socket.on("close", () => {
                    this.clearSocketListeners();
                    this.authenticated = false;
                    this.socket = null;
                    setTimeout(() => this.initSocket(), 3000); // Reconnect after 3 seconds
                });
                this.socket.on("error", error => {
                    console.error("WebSocket error:", error);
                    if (this.protocol === "v2") {
                        window.dispatchEvent(new CustomEvent("socketError", { detail: error }));
                    }
                });
                this.socket.on("disconnect", () => {
                    if (this.socketId) {
                        SocketService.instances.delete(this.socketId);
                    }
                    this.authenticated = false;
                });
            });
            // Catch so callers don't get unhandled promise rejection — error already dispatched via socketError event
            this.connectionPromise = this.connectionPromise.catch(err => {
                console.error("Socket connection failed:", err.message);
            });
        }
    }
    // Helper to remove all listeners from the current socket instance
    clearSocketListeners() {
        if (this.socket) {
            // Removes all listeners for specific events
            this.socket.off("connect");
            this.socket.off("response");
            this.socket.off("end_response");
            this.socket.off("chunk");
            this.socket.off("complete");
            this.socket.off("authenticated");
            this.socket.off("close");
            this.socket.off("error");
            this.socket.off("disconnect");
        }
    }
    static createInstance(mode, apiKey = "") {
        const instance = new SocketService(mode, apiKey);
        return instance;
    }
    static getInstanceById(socketId) {
        return SocketService.instances.get(socketId);
    }
    getSocket() {
        return this.socket;
    }
    getSocketId() {
        return this.socketId;
    }
    getProtocol() {
        return this.protocol;
    }
    isAuthenticated() {
        return this.protocol === "legacy" || this.authenticated;
    }
    // Wait for socket connection to complete
    async waitForConnection() {
        if (this.connectionPromise) {
            await this.connectionPromise;
        }
    }
    // Check if socket is connected
    isConnected() {
        var _a, _b;
        return (_b = (_a = this.socket) === null || _a === void 0 ? void 0 : _a.connected) !== null && _b !== void 0 ? _b : false;
    }
    disconnect() {
        if (this.socket) {
            this.clearSocketListeners(); // Remove all listeners before disconnecting
            this.socket.disconnect();
            this.socket = null;
        }
        if (this.socketId) {
            SocketService.instances.delete(this.socketId);
            this.socketId = ""; // Clear socketId as well
        }
        this.authenticated = false;
        // console.log("Socket disconnected")
    }
    static disconnectSocketById(socketId) {
        const instance = SocketService.instances.get(socketId);
        if (instance && instance.socket) {
            instance.socket.disconnect();
            SocketService.instances.delete(socketId);
            // console.log(`Socket with ID ${socketId} disconnected`)
        }
        else {
            console.error(`Socket with ID ${socketId} not found`);
        }
    }
}
//   private static instance: SocketService
SocketService.instances = new Map();
