#!/usr/bin/env node
import assert from 'assert';
import fs from 'fs';
import net from 'net';
import os from 'os';
import path from 'path';
import { WebServer } from './src/server/server.js';
import { findFreePort } from './src/server/helpers.js';

function listenOnPort(port) {
  return new Promise((resolve, reject) => {
    const server = net.createServer();
    server.once('error', reject);
    server.listen({ port, exclusive: true }, () => resolve(server));
  });
}

function closeNetServer(server) {
  return new Promise((resolve) => server.close(() => resolve()));
}

function createStubProjectContext(workDir) {
  return {
    workDir,
    projectMeta: null,
    async load() {},
    async scan() {},
    isVLProject() { return false; },
    getSummary() {
      return { projectName: null, totalFiles: 0, breakdown: '', version: null };
    },
    getAllFiles() { return []; },
    getProjectMeta() { return null; },
  };
}

async function main() {
  const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vlcode-port-retry-'));
  const occupiedPort = await findFreePort(4070, 4098);
  const blocker = await listenOnPort(occupiedPort);

  const contextManager = {
    messages: [],
    turnCount: 0,
    compressionCount: 0,
    totalTokensSaved: 0,
    totalInputTokens: 0,
    totalOutputTokens: 0,
    estimateTokens() {},
  };

  const server = new WebServer({
    config: {
      workDir,
      noWorkspace: true,
      emptyWorkspaceDir: workDir,
      model: 'test-model',
      port: occupiedPort,
      _undoStack: [],
    },
    orchestrator: null,
    projectContext: createStubProjectContext(workDir),
    contextManager,
    toolRegistry: {
      clear() {},
      getToolSchemas() { return []; },
      async execute() { return {}; },
    },
    symbolIndex: {
      async build() {},
      getStats() { return { totalSymbols: 0, totalReferences: 0 }; },
    },
    impactAnalyzer: null,
    autoFix: null,
    localWorkspace: {},
    blueprintContext: { async load() {} },
  });

  try {
    const boundPort = await server.start(occupiedPort);
    assert.strictEqual(boundPort, occupiedPort + 1, 'WebServer.start should retry the next port after EADDRINUSE');
    assert.strictEqual(server._serverPort, occupiedPort + 1, 'server state should track the retried port');
    console.log('\n── WebServer Port Retry Regression ──');
    console.log('PASS test-webserver-port-retry.js');
  } finally {
    await server.stop().catch(() => {});
    await closeNetServer(blocker).catch(() => {});
    fs.rmSync(workDir, { recursive: true, force: true });
  }
}

main().catch((err) => {
  console.error(err.stack || err.message);
  process.exit(1);
});
