/**
 * Copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import net from 'net';

export class SocketConnection {
  private _socket: net.Socket;
  private _pendingBuffers: Buffer[] = [];

  onclose?: () => void;
  onmessage?: (message: any) => void;

  constructor(socket: net.Socket) {
    this._socket = socket;
    socket.on('data', buffer => this._onData(buffer));
    socket.on('close', () => {
      this.onclose?.();
    });

    // eslint-disable-next-line no-console
    socket.on('error', e => console.error(`error: ${e.message}`));
  }

  async send(message: { id: number, error?: string, result?: any }) {
    await new Promise((resolve, reject) => {
      this._socket.write(`${JSON.stringify(message)}\n`, error => {
        if (error)
          reject(error);
        else
          resolve(undefined);
      });
    });
  }

  close() {
    this._socket.destroy();
  }

  private _onData(buffer: Buffer) {
    let end = buffer.indexOf('\n');
    if (end === -1) {
      this._pendingBuffers.push(buffer);
      return;
    }
    this._pendingBuffers.push(buffer.slice(0, end));
    const message = Buffer.concat(this._pendingBuffers).toString();
    this._dispatchMessage(message);

    let start = end + 1;
    end = buffer.indexOf('\n', start);
    while (end !== -1) {
      const message = buffer.toString(undefined, start, end);
      this._dispatchMessage(message);
      start = end + 1;
      end = buffer.indexOf('\n', start);
    }
    this._pendingBuffers = [buffer.slice(start)];
  }

  private _dispatchMessage(message: string) {
    try {
      this.onmessage?.(JSON.parse(message));
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error('failed to dispatch message', e);
    }
  }
}

export function compareSemver(a: string, b: string): number {
  const aBase = a.replace(/-.*$/, '');
  const bBase = b.replace(/-.*$/, '');
  const aParts = aBase.split('.').map(Number);
  const bParts = bBase.split('.').map(Number);
  for (let i = 0; i < 3; i++) {
    if (aParts[i] > bParts[i])
      return 1;
    if (aParts[i] < bParts[i])
      return -1;
  }
  const aTimestamp = parseSuffixTimestamp(a);
  const bTimestamp = parseSuffixTimestamp(b);
  if (aTimestamp > bTimestamp)
    return 1;
  if (aTimestamp < bTimestamp)
    return -1;
  return 0;
}

function parseSuffixTimestamp(version: string): number {
  // Stable release (no suffix) is greater than any pre-release.
  const match = version.match(/^\d+\.\d+\.\d+-(?:alpha|beta)-(.+)$/);
  if (!match)
    return Infinity;
  const suffix = match[1];
  // Daily alpha: 1.59.0-alpha-2026-02-16
  if (/^\d{4}-\d{2}-\d{2}$/.test(suffix))
    return new Date(suffix).getTime();
  // Nightly/per-commit: 1.59.0-alpha-1771260841000
  return Number(suffix);
}
