import net from 'node:net';
import { spawn } from 'node:child_process';

const HOST = process.env.VITE_DEV_HOST ?? '127.0.0.1';
const PORT = Number(process.env.VITE_DEV_PORT ?? '8080');
const VITE_BIN = process.platform === 'win32' ? 'vite.cmd' : 'vite';
const VITE_ARGS = ['--host', HOST, '--port', String(PORT), '--strictPort'];

function isPortOccupied(port, host) {
  return new Promise((resolve) => {
    const socket = net.createConnection({ port, host });

    const finish = (occupied) => {
      socket.removeAllListeners();
      socket.destroy();
      resolve(occupied);
    };

    socket.once('connect', () => finish(true));
    socket.once('timeout', () => finish(false));
    socket.once('error', (error) => {
      if (error && (error.code === 'ECONNREFUSED' || error.code === 'EHOSTUNREACH')) {
        finish(false);
        return;
      }

      finish(true);
    });
    socket.setTimeout(500);
  });
}

function waitForShutdown() {
  console.log(`[ui] Port ${PORT} is already in use; assuming the UI dev server is already running and skipping Vite startup.`);

  const interval = setInterval(() => {}, 60_000);
  const shutdown = () => {
    clearInterval(interval);
    process.exit(0);
  };

  process.on('SIGINT', shutdown);
  process.on('SIGTERM', shutdown);
}

async function main() {
  const occupied = await isPortOccupied(PORT, HOST);
  if (occupied) {
    waitForShutdown();
    return;
  }

  const child = spawn(VITE_BIN, VITE_ARGS, {
    stdio: 'inherit',
    env: process.env,
  });

  const forwardSignal = (signal) => {
    if (!child.killed) {
      child.kill(signal);
    }
  };

  process.on('SIGINT', () => forwardSignal('SIGINT'));
  process.on('SIGTERM', () => forwardSignal('SIGTERM'));

  child.on('exit', (code, signal) => {
    if (signal) {
      process.kill(process.pid, signal);
      return;
    }

    process.exit(code ?? 0);
  });
}

main().catch((error) => {
  console.error('[ui] Failed to prepare Vite dev server:', error);
  process.exit(1);
});