import {
  toSocket,
  WebSocketMessageReader,
  WebSocketMessageWriter,
} from "vscode-ws-jsonrpc";
import {
  CloseAction,
  ErrorAction,
  MessageTransports,
} from "vscode-languageclient";
import { RefObject } from "react";
import normalizeUrl from "normalize-url";
import { MonacoLanguageClient } from "monaco-languageclient";

const LSP_SUPPORTED_LANGUAGES: {
  [key: string]: { hostname: string; port: number; path: string };
} = {
  c: {
    hostname: "localhost",
    port: 30001,
    path: "/clangd",
  },
  cpp: {
    hostname: "localhost",
    port: 30002,
    path: "/clangd",
  },
};

function createUrl(hostname: string, port: number, path: string): string {
  const protocol = location.protocol === "https:" ? "wss" : "ws";
  return normalizeUrl(`${protocol}://${hostname}:${port}${path}`);
}

const languageClientConfig = {
  name: "Language Client",
  clientOptions: {
    documentSelector: ["c", "cpp"],
    errorHandler: {
      error: () => ({ action: ErrorAction.Continue }),
      closed: () => ({ action: CloseAction.DoNotRestart }),
    },
  },
};

function createLanguageClient(
  transports: MessageTransports
): MonacoLanguageClient {
  return new MonacoLanguageClient({
    ...languageClientConfig,
    connectionProvider: {
      get: () => Promise.resolve(transports),
    },
  });
}

export async function connectToLanguageServer(
  language: string,
  webSocketRef: RefObject<WebSocket | null>
): Promise<MonacoLanguageClient> {
  return new Promise((resolve, reject) => {
    let url: string;
    if (language in LSP_SUPPORTED_LANGUAGES) {
      const { hostname, port, path } = LSP_SUPPORTED_LANGUAGES[language];
      url = createUrl(hostname, port, path);
    } else {
      reject(new Error("Unsupported language"));
      return;
    }

    const webSocket = new WebSocket(url);
    webSocketRef.current = webSocket;

    webSocket.onopen = () => {
      console.log("WebSocket connection opened successfully.");
      const socket = toSocket(webSocket);
      const reader = new WebSocketMessageReader(socket);
      const writer = new WebSocketMessageWriter(socket);
      const languageClient = createLanguageClient({ reader, writer });
      languageClient.start();
      resolve(languageClient);
    };

    webSocket.onclose = () => {
      console.log("WebSocket connection closed.");
      reject(new Error("WebSocket connection closed unexpectedly."));
    };

    webSocket.onerror = (error) => {
      console.error("WebSocket connection error:", error);
      reject(new Error(`WebSocket connection error: ${error}`));
    };
  });
}
