import { Hono } from 'hono';
import {
  ShadeSessionManager,
  ShadeEventEmitter,
} from '../../packages/shade-core/src/index.js';
import {
  SubtleCryptoProvider,
  MemoryStorage,
} from '../../packages/shade-crypto-web/src/index.js';
import {
  createPrekeyServer,
  MemoryPrekeyStore,
  PrekeyServerEvents,
} from '../../packages/shade-server/src/index.js';
import { createObserver } from '../../packages/shade-observer/src/index.js';

const crypto = new SubtleCryptoProvider();
const TOKEN = 'demo-token-must-be-at-least-16-chars';

async function main() {
  console.log('━━━ Shade Observer Demo ━━━\n');

  // ─── Wire up event emitters ──────────────────────────
  const clientEvents = new ShadeEventEmitter();
  const serverEvents = new PrekeyServerEvents();

  // ─── Two demo session managers ───────────────────────
  const alice = new ShadeSessionManager(crypto, new MemoryStorage(), { events: clientEvents });
  const bob = new ShadeSessionManager(crypto, new MemoryStorage(), { events: clientEvents });
  await alice.initialize();
  await bob.initialize();

  // ─── Prekey server with events ───────────────────────
  const prekeyApp = createPrekeyServer({
    crypto,
    store: new MemoryPrekeyStore(),
    disableRateLimit: true,
    events: serverEvents,
  });

  // ─── Observer ────────────────────────────────────────
  const observer = createObserver({
    token: TOKEN,
    clientEvents,
    serverEvents,
  });

  // ─── Mount everything in one Hono app ────────────────
  const app = new Hono();
  app.route('/prekey', prekeyApp);
  app.route('/', observer);

  const PORT = 3901;
  Bun.serve({ port: PORT, fetch: app.fetch });

  console.log(`✓ Server listening on http://localhost:${PORT}`);
  console.log(`✓ Dashboard:  http://localhost:${PORT}/dashboard/`);
  console.log(`✓ Token:      ${TOKEN}\n`);
  console.log('Open the dashboard, paste the token, and watch the live activity below…\n');

  // ─── Establish Alice ↔ Bob session ───────────────────
  await bob.generateOneTimePreKeys(20);
  const bundle = await bob.createPreKeyBundle();
  // Inline a one-time prekey since we're not going through the prekey server
  const otpks = await bob.generateOneTimePreKeys(1);
  bundle.oneTimePreKey = { keyId: otpks[0].keyId, publicKey: otpks[0].keyPair.publicKey };
  await alice.initSessionFromBundle('bob', bundle);

  // Initial exchange to establish bidirectional ratchet
  const initEnv = await alice.encrypt('bob', 'init');
  await bob.decrypt('alice', initEnv);
  const initReply = await bob.encrypt('alice', 'init reply');
  await alice.decrypt('bob', initReply);

  // ─── Run a loop of encrypted messages ────────────────
  const messages = [
    "Hey Bob, can you see this?",
    "Yes Alice, loud and clear.",
    "Cool — every message has a fresh key.",
    "And the dashboard shows it live.",
    "Ratchet steps every time we switch direction.",
    "Forward secrecy in action.",
  ];

  let i = 0;
  setInterval(async () => {
    try {
      const fromAlice = i % 2 === 0;
      const sender = fromAlice ? alice : bob;
      const receiver = fromAlice ? bob : alice;
      const senderAddr = fromAlice ? 'bob' : 'alice';
      const receiverAddr = fromAlice ? 'alice' : 'bob';
      const text = messages[i % messages.length];

      const env = await sender.encrypt(senderAddr, text);
      await receiver.decrypt(receiverAddr, env);
      console.log(`  [${i + 1}] ${fromAlice ? 'Alice' : 'Bob  '}: "${text}"`);
      i++;

      // Periodically replenish prekeys to show that activity in the dashboard
      if (i % 8 === 0) {
        await bob.ensurePreKeyStock(5, 20);
      }
    } catch (err) {
      console.error('Loop error:', err);
    }
  }, 2000);
}

main().catch(console.error);
