/**
 * FlowPad Cross-Realm Integration Test
 *
 * This test verifies the EXACT pattern FlowPad uses for cross-realm communication:
 * 1. FlowPad (browser) creates CrossRealmRouter with tab realm
 * 2. FlowPad calls viaRemote("daemon@matrix-3/daemon.matrix-3", "introspect")
 * 3. CrossRealmRouter routes to CrossRealmClient (cross-realm)
 * 4. CrossRealmClient publishes to daemon's mailbox topic
 * 5. Daemon's EdgeGateway receives, routes to internal handler
 * 6. Handler responds, EdgeGateway sends reply
 * 7. FlowPad's CrossRealmClient receives reply
 *
 * This is a LOOPBACK test - both FlowPad and daemon are simulated in-process
 * connected via a shared InMemory backbone.
 */

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { FederationPeer, TabInstanceResolver } from '@open-matrix/federation/federation_peer';
import { InMemoryFilterBroker } from '@open-matrix/federation/transport/inmem';
import { WIRE_PROFILES } from '@open-matrix/federation/wire_profile';
import { getInternalTopic } from '@open-matrix/federation/topics';

/**
 * Test the exact FlowPad pattern: viaRemote to daemon@matrix-3
 */
test('FlowPad cross-realm to daemon@matrix-3 (full loopback)', async () => {
  // ═══════════════════════════════════════════════════════════════════════════
  // BACKBONE: Shared InMemory broker simulating HiveMQ
  // ═══════════════════════════════════════════════════════════════════════════
  const backbone = new InMemoryFilterBroker();

  // ═══════════════════════════════════════════════════════════════════════════
  // FLOWPAD SIDE: CrossRealmRouter with tab realm
  // ═══════════════════════════════════════════════════════════════════════════
  const flowpadLocalBus = new InMemoryFilterBroker();

  // FlowPad's tab-isolated realm
  const flowpadTabRealm = 'flowpad.tab-abc123';

  const flowpadRouter = new FederationPeer({
    localRealm: flowpadTabRealm,
    localBus: flowpadLocalBus,
    instanceResolver: new TabInstanceResolver(flowpadTabRealm),
    backbone: backbone, // Connect to shared backbone
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // DAEMON SIDE: EdgeGateway processing incoming requests
  // ═══════════════════════════════════════════════════════════════════════════
  const daemonLocalBus = new InMemoryFilterBroker();
  const daemonRealm = 'daemon@matrix-3';

  // Create EdgeGateway for daemon realm
  const daemonEdge = new FederationPeer({
    localRealm: daemonRealm,
    backbone,
    localBus: daemonLocalBus,
    profile: WIRE_PROFILES.mxenv1,
    internalTimeoutMs: 3000,
  });

  // Start daemon EdgeGateway (subscribes to mailbox topics)
  daemonEdge.start();

  // Set up daemon's internal handler for introspect
  const daemonInternalTopic = getInternalTopic(daemonRealm, 'daemon.matrix-3.accepts.introspect');
  console.log(`[Test] Daemon subscribing to: ${daemonInternalTopic}`);

  daemonLocalBus.subscribe(daemonInternalTopic, (topic, payload) => {
    console.log(`[Test] Daemon received on ${topic}:`, payload);
    const request = JSON.parse(payload);

    // Send reply to the replyTo topic
    const reply = {
      correlationId: request.correlationId,
      ok: true,
      result: {
        type: 'DaemonProject',
        name: 'matrix-3',
        children: ['file1.ts', 'file2.ts'],
      },
    };

    console.log(`[Test] Daemon replying to: ${request.replyTo}`);
    daemonLocalBus.publish(request.replyTo, JSON.stringify(reply));
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // TEST: FlowPad calls viaRemote to daemon
  // ═══════════════════════════════════════════════════════════════════════════
  console.log(`[Test] FlowPad invoking: daemon@matrix-3/daemon.matrix-3.accepts.introspect`);

  const result = await flowpadRouter.invoke<{ type: string; name: string; children: string[] }>(
    'daemon@matrix-3/daemon.matrix-3.accepts.introspect',
    {},
    5000
  );

  console.log(`[Test] Result:`, result);

  // Verify result
  assert.equal(result.type, 'DaemonProject');
  assert.equal(result.name, 'matrix-3');
  assert.deepEqual(result.children, ['file1.ts', 'file2.ts']);

  // Cleanup
  daemonEdge.stop();
  flowpadRouter.stop();
});

/**
 * Test that base realm matching works: flowpad → flowpad.tab-xxx is LOCAL
 */
test('FlowPad local routing: flowpad → flowpad.tab-xxx is LOCAL', async () => {
  const localBus = new InMemoryFilterBroker();

  const tabRealm = 'flowpad.tab-abc123';

  const router = new FederationPeer({
    localRealm: tabRealm,
    localBus: localBus,
    instanceResolver: new TabInstanceResolver(tabRealm),
  });

  // Set up local handler on the tab realm topic using browser local bus format.
  // TabInstanceResolver returns semantic path for browser local bus.
  const localTopic = `${tabRealm}.tree.accepts.introspect`;
  console.log(`[Test] Local handler subscribing to: ${localTopic}`);

  localBus.subscribe(localTopic, (topic, payload) => {
    console.log(`[Test] Local handler received on ${topic}`);
    const request = JSON.parse(payload);
    const reply = {
      ok: true,
      result: { type: 'FractalTree', local: true },
    };
    localBus.publish(request.replyTo, JSON.stringify(reply));
  });

  // FlowPad uses base realm "flowpad" in the URI (not the full tab realm)
  // The router should still route locally because "flowpad" is the base of "flowpad.tab-abc123"
  const result = await router.invoke<{ type: string; local: boolean }>(
    'flowpad/tree.accepts.introspect',
    {},
    1000
  );

  console.log(`[Test] Result:`, result);

  assert.equal(result.type, 'FractalTree');
  assert.equal(result.local, true);

  router.stop();
});

/**
 * Test different realm = CROSS-REALM (not local)
 */
test('FlowPad to daemon is CROSS-REALM (not local)', async () => {
  const localBus = new InMemoryFilterBroker();

  const tabRealm = 'flowpad.tab-abc123';

  const router = new FederationPeer({
    localRealm: tabRealm,
    localBus: localBus,
    instanceResolver: new TabInstanceResolver(tabRealm),
    // No backbone transport - should throw on cross-realm
  });

  // daemon@matrix-3 is a DIFFERENT realm - should require backbone
  await assert.rejects(
    () => router.invoke('daemon@matrix-3/daemon.matrix-3.accepts.introspect', {}, 1000),
    /No backbone transport/
  );

  router.stop();
});
