import { describe, expect, it } from 'bun:test';
import { cliampUpgradeData, musicWebsocket } from './cliamp-ws';

// The player socket refuses a path before it spawns anything, so these two cases exercise the whole
// server → handler → frame path without starting cliamp. Anything that would actually play needs a real
// file and a real audio sink, so it is not tested here.

function serveOnce() {
  const server = Bun.serve({
    port: 0,
    hostname: '127.0.0.1',
    fetch(req, srv) {
      const url = new URL(req.url);
      const data = cliampUpgradeData(url.pathname, url.searchParams);
      if (data && srv.upgrade(req, { data })) return undefined as unknown as Response;
      return new Response('nope', { status: 400 });
    },
    websocket: musicWebsocket,
  });
  return server;
}

function firstFrame(url: string): Promise<string> {
  return new Promise((resolve, reject) => {
    const ws = new WebSocket(url);
    const timer = setTimeout(() => reject(new Error('no frame')), 3000);
    ws.addEventListener('message', (ev) => {
      clearTimeout(timer);
      ws.close();
      resolve(String(ev.data));
    });
    ws.addEventListener('error', () => {
      clearTimeout(timer);
      reject(new Error('socket error'));
    });
  });
}

describe('cliamp player socket', () => {
  it('rejects a path that escapes the owner home', async () => {
    const server = serveOnce();
    try {
      const frame = await firstFrame(`ws://127.0.0.1:${server.port}/cliamp/ws?files=../../etc/passwd`);
      expect(JSON.parse(frame)).toEqual({ type: 'output', data: '\r\n[Error] Invalid file path.\r\n' });
    } finally {
      server.stop(true);
    }
  });

  it('reports a missing files param instead of spawning', async () => {
    const server = serveOnce();
    try {
      const frame = await firstFrame(`ws://127.0.0.1:${server.port}/cliamp/ws`);
      expect(JSON.parse(frame)).toEqual({ type: 'output', data: '\r\n[Error] No files specified.\r\n' });
    } finally {
      server.stop(true);
    }
  });

  it('routes only the two cliamp paths', () => {
    const q = new URLSearchParams();
    expect(cliampUpgradeData('/cliamp/ws', q)).toEqual({ kind: 'player', files: '' });
    expect(cliampUpgradeData('/cliamp/audio/ws', q)).toEqual({ kind: 'capture' });
    expect(cliampUpgradeData('/stream', q)).toBeNull();
  });
});
