import { ShadeSessionManager } from '../../packages/shade-core/src/index.js';
import { SubtleCryptoProvider, MemoryStorage } from '../../packages/shade-crypto-web/src/index.js';
import { ShadeWebSocket } from '../../packages/shade-transport/src/index.js';

const crypto = new SubtleCryptoProvider();

async function main() {
  console.log('=== Shade WebSocket Tunnel ===\n');

  // Set up Alice and Bob
  const alice = new ShadeSessionManager(crypto, new MemoryStorage());
  const bob = new ShadeSessionManager(crypto, new MemoryStorage());
  await alice.initialize();
  await bob.initialize();

  // Establish session (skipping prekey server for brevity)
  const otpks = await bob.generateOneTimePreKeys(5);
  const bundle = await bob.createPreKeyBundle();
  bundle.oneTimePreKey = { keyId: otpks[0].keyId, publicKey: otpks[0].keyPair.publicKey };
  await alice.initSessionFromBundle('bob', bundle);

  // Pair of WebSockets connected to each other
  // Use Bun's built-in WebSocket server + client for the demo
  const port = 19851;
  const messages: string[] = [];

  const server = Bun.serve({
    port,
    fetch(req, srv) {
      if (srv.upgrade(req)) return;
      return new Response('upgrade failed', { status: 500 });
    },
    websocket: {
      async message(ws, message) {
        // Server side decrypts (acts as Bob)
        const bytes = message instanceof Uint8Array ? message : new Uint8Array(Buffer.from(message as string));
        // Need to manually use ShadeWebSocket or do raw decoding
        console.log(`[Bob received ${bytes.length} encrypted bytes]`);
        // Forward to Bob's session
        const { decodeEnvelope } = await import('../../packages/shade-proto/src/index.js');
        const envelope = decodeEnvelope(bytes);
        const plain = await bob.decrypt('alice', envelope);
        messages.push(plain);
        console.log(`[Bob decrypted]: "${plain}"`);
      },
    },
  });

  // Alice opens a WebSocket and uses ShadeWebSocket wrapper
  const aliceWs = new WebSocket(`ws://localhost:${port}/`);
  await new Promise((r) => aliceWs.addEventListener('open', r));

  const aliceShade = new ShadeWebSocket(aliceWs, alice, 'bob');

  console.log('Alice → Bob: "Hello over WebSocket"');
  await aliceShade.send('Hello over WebSocket');

  console.log('Alice → Bob: "Second message"');
  await aliceShade.send('Second message');

  // Wait for messages to arrive
  await new Promise((r) => setTimeout(r, 200));

  console.log('\nMessages Bob received:', messages);
  console.log('\n=== Done ===');

  aliceShade.close();
  server.stop();
}

main().catch(console.error);
