import * as net from "node:net";
import * as vscode from "vscode";

import {
    LanguageClient,
    LanguageClientOptions,
    ServerOptions,
    StreamInfo
} from "vscode-languageclient/node";

let client: LanguageClient | undefined;
let output: vscode.OutputChannel;

export function activate(context: vscode.ExtensionContext): void {
    output = vscode.window.createOutputChannel("Prometeu LSP");
    output.appendLine("Prometeu VS Code extension activated.");

    const connectCommand = vscode.commands.registerCommand(
        "prometeu.connectStudioLsp",
        async () => {
            await startClient(context);
        }
    );

    const restartCommand = vscode.commands.registerCommand(
        "prometeu.restartStudioLsp",
        async () => {
            await stopClient();
            await startClient(context);
        }
    );

    context.subscriptions.push(output, connectCommand, restartCommand);

    void startClient(context);
}

export async function deactivate(): Promise<void> {
    await stopClient();
}

async function startClient(context: vscode.ExtensionContext): Promise<void> {
    if (client) {
        output.appendLine("Prometeu LSP client is already running.");
        return;
    }

    const config = vscode.workspace.getConfiguration("prometeu.studio");

    const host = config.get<string>("host", "127.0.0.1");
    const port = config.get<number>("port", 7777);

    output.show(true);
    output.appendLine(`Connecting to Prometeu Studio LSP at ${host}:${port}...`);

    const serverOptions: ServerOptions = async (): Promise<StreamInfo> => {
        return new Promise<StreamInfo>((resolve, reject) => {
            const socket = net.createConnection({ host, port });

            let settled = false;

            const connectTimeout = setTimeout(() => {
                if (settled) {
                    return;
                }

                settled = true;
                socket.destroy();
                reject(new Error("Connection timed out."));
            }, 3000);

            socket.once("connect", () => {
                if (settled) {
                    return;
                }

                settled = true;
                clearTimeout(connectTimeout);

                output.appendLine("TCP connection established with Prometeu Studio.");

                resolve({
                    reader: socket,
                    writer: socket
                });
            });

            socket.once("error", (error) => {
                if (settled) {
                    return;
                }

                settled = true;
                clearTimeout(connectTimeout);

                reject(error);
            });
        });
    };

    const clientOptions: LanguageClientOptions = {
        documentSelector: [
            {
                scheme: "file",
                language: "pbs"
            }
        ],
        synchronize: {
            fileEvents: [
                vscode.workspace.createFileSystemWatcher("**/*.pbs")
            ]
        },
        initializationOptions: {
            clientKind: "vscode",
            prometeuClientVersion: context.extension.packageJSON.version
        },
        outputChannel: output
    };

    client = new LanguageClient(
        "prometeuStudioLsp",
        "Prometeu Studio LSP",
        serverOptions,
        clientOptions
    );

    try {
        await client.start();

        output.appendLine("Prometeu Studio LSP client started.");
        vscode.window.showInformationMessage("Connected to Prometeu Studio LSP.");
    } catch (error) {
        client = undefined;

        const message = error instanceof Error ? error.message : String(error);

        output.appendLine(`Failed to start Prometeu LSP client: ${message}`);
        vscode.window.showErrorMessage(`Could not connect to Prometeu Studio LSP: ${message}`);
    }
}

async function stopClient(): Promise<void> {
    if (!client) {
        return;
    }

    const currentClient = client;
    client = undefined;

    output.appendLine("Stopping Prometeu Studio LSP client...");

    try {
        await currentClient.stop();
        output.appendLine("Prometeu Studio LSP client stopped.");
    } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        output.appendLine(`Failed to stop Prometeu LSP client cleanly: ${message}`);
    }
}