/********************************************************************************
 * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available in the project root.
 *
 * SPDX-License-Identifier: MIT
 ********************************************************************************/

import { GLSPContribution } from '@eclipse-glsp/theia-integration/lib/common';
import { AbstractSocketForwardingConnectionHandler } from '@hydranium/client-theia/node';
import { injectable, unmanaged } from '@theia/core/shared/inversify';
import type * as net from 'net';

/** Options for `GlspServerConnectionHandler`. The language contribution
 *  id determines the per-language servicePath that Theia routes browser-frontend
 *  connections to; the port command is the Theia/VS Code command id whose return
 *  value is the GLSP server's listening port. */
export interface GlspServerConnectionHandlerOptions {
   readonly languageContributionId: string;
   readonly portCommand: string;
   /** Name of the server in this handler's log lines. */
   readonly serverName?: string;
   readonly findPortTimeout?: number;
   readonly findPortAttempts?: number;
   readonly connectTimeoutMs?: number;
   /**
    * Optional diagnostic hook called immediately after the outbound
    * `net.Socket` to the GLSP server is created, BEFORE `socket.connect()`
    * is invoked. Adopters use this to attach `'data'` / `'close'` listeners
    * for byte-level observability when debugging wire-level handshake
    * issues; attaching listeners before `connect()` is what guarantees the
    * very first bytes are observed.
    */
   readonly onSocketCreated?: (socket: net.Socket) => void;
}

/**
 * Bridges a Theia browser-frontend channel to a GLSP server's TCP socket.
 *
 * The port discovery, the buffer-and-replay race fix, the connect orchestration
 * and the byte forwarder live on the cross-head
 * {@link AbstractSocketForwardingConnectionHandler} base; this subclass supplies
 * only the GLSP specifics — the per-language servicePath
 * (`GLSPContribution.servicePath + '/' + languageContributionId`), the port
 * command, the log labels. Adopters with different language ids or command
 * names subclass with a one-line `super({...})` call.
 */
@injectable()
export class GlspServerConnectionHandler extends AbstractSocketForwardingConnectionHandler {
   constructor(@unmanaged() options: GlspServerConnectionHandlerOptions) {
      super({
         path: GLSPContribution.servicePath + '/' + options.languageContributionId,
         portCommand: options.portCommand,
         logComponent: 'GLSP',
         serverName: options.serverName ?? 'Diagram Server',
         findPortTimeout: options.findPortTimeout,
         findPortAttempts: options.findPortAttempts,
         connectTimeoutMs: options.connectTimeoutMs,
         onSocketCreated: options.onSocketCreated
      });
   }

   /** Logged only: the frontend contribution reports the failed start, and a
    *  second notification would duplicate it. */
   protected override reportConnectFailure(): void {}
}
