/**
 * Cross-Realm Two-Gateway Integration Test
 *
 * Tests actual cross-realm communication where:
 * - Realm A's EdgeGateway sends a message
 * - Backbone routes it to Realm B's mailbox
 * - Realm B's EdgeGateway receives and processes it
 * - Reply flows back to Realm A
 */

import { describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
import {
  FederationPeer, ExactInstanceResolver,
  WIRE_PROFILES,
  InMemoryFilterBroker,
  getLegacyIngressTopic,
  createLegacyRequest,
  serializeLegacyEnvelope,
} from '@open-matrix/federation';

describe('Cross-Realm Two Gateways', () => {

  it('Realm A sends request to Realm B and receives reply', async () => {
    const REALM_A = 'realm-alpha';
    const REALM_B = 'realm-beta';

    // Shared backbone broker (simulates HiveMQ)
    const backbone = new InMemoryFilterBroker();

    // Realm A's local bus
    const localBusA = new InMemoryFilterBroker();

    // Realm B's local bus
    const localBusB = new InMemoryFilterBroker();

    // Create EdgeGateway for Realm A
    const gatewayA = new FederationPeer({
      localRealm: REALM_A,
      profile: WIRE_PROFILES.matrix3,
      backbone,
      localBus: localBusA,
      internalTimeoutMs: 5000,
    });

    // Create EdgeGateway for Realm B
    const gatewayB = new FederationPeer({
      localRealm: REALM_B,
      profile: WIRE_PROFILES.matrix3,
      backbone,
      localBus: localBusB,
      internalTimeoutMs: 5000,
    });

    // Start both gateways
    gatewayA.start();
    gatewayB.start();

    // Set up handler on Realm B's local bus
    localBusB.subscribe(`calculator.accepts.add`, (topic, payload) => {
      const req = JSON.parse(payload) as {
        replyTo: string;
        correlationId: string;
        params: { a: number; b: number };
      };

      // Calculate result
      const result = req.params.a + req.params.b;

      // Send reply
      localBusB.publish(req.replyTo, JSON.stringify({
        ok: true,
        correlationId: req.correlationId,
        result: { sum: result },
      }));
    });

    // Set up listener for reply on Realm A's mailbox
    let receivedReply: unknown = null;
    backbone.subscribe(getLegacyIngressTopic(REALM_A), (topic, payload) => {
      const parsed = JSON.parse(payload);
      if (parsed.ok !== undefined) {  // It's a reply
        receivedReply = parsed;
      }
    });

    await new Promise(r => setTimeout(r, 100));

    // Realm A sends request to Realm B
    const request = createLegacyRequest({
      toRealm: REALM_B,
      toPath: 'calculator.accepts.add',
      fromRealm: REALM_A,
      replyTo: '_client.inbox',
      correlationId: 'cross-realm-123',
      body: { a: 10, b: 32 },
    });

    // Publish to Realm B's mailbox on backbone
    backbone.publish(getLegacyIngressTopic(REALM_B), serializeLegacyEnvelope(request));

    // Wait for round-trip
    await new Promise(r => setTimeout(r, 500));

    // Verify reply was received
    assert.ok(receivedReply, 'Should receive reply from Realm B');
    assert.strictEqual((receivedReply as { ok: boolean }).ok, true, 'Reply should be ok');
    assert.deepStrictEqual(
      (receivedReply as { body: { sum: number } }).body,
      { sum: 42 },
      'Sum should be 42'
    );

    console.log('✓ Cross-realm request/reply successful: 10 + 32 = 42');

    gatewayA.stop();
    gatewayB.stop();
  });

  it('Three realms: A → B → C chain', async () => {
    const REALM_A = 'realm-a';
    const REALM_B = 'realm-b';
    const REALM_C = 'realm-c';

    const backbone = new InMemoryFilterBroker();
    const localBusA = new InMemoryFilterBroker();
    const localBusB = new InMemoryFilterBroker();
    const localBusC = new InMemoryFilterBroker();

    const gatewayA = new FederationPeer({
      localRealm: REALM_A,
      profile: WIRE_PROFILES.matrix3,
      backbone,
      localBus: localBusA,
      internalTimeoutMs: 5000,
    });

    const gatewayB = new FederationPeer({
      localRealm: REALM_B,
      profile: WIRE_PROFILES.matrix3,
      backbone,
      localBus: localBusB,
      internalTimeoutMs: 5000,
    });

    const gatewayC = new FederationPeer({
      localRealm: REALM_C,
      profile: WIRE_PROFILES.matrix3,
      backbone,
      localBus: localBusC,
      internalTimeoutMs: 5000,
    });

    gatewayA.start();
    gatewayB.start();
    gatewayC.start();

    // Handler on Realm C
    localBusC.subscribe(`service.accepts.echo`, (topic, payload) => {
      const req = JSON.parse(payload) as { replyTo: string; correlationId: string; params: { msg: string } };
      localBusC.publish(req.replyTo, JSON.stringify({
        ok: true,
        correlationId: req.correlationId,
        result: { echo: `Echo from C: ${req.params.msg}` },
      }));
    });

    // Collect replies at Realm A
    let replyFromC: unknown = null;
    backbone.subscribe(getLegacyIngressTopic(REALM_A), (topic, payload) => {
      const parsed = JSON.parse(payload);
      if (parsed.ok !== undefined) {
        replyFromC = parsed;
      }
    });

    await new Promise(r => setTimeout(r, 100));

    // A sends directly to C (skipping B - this tests that each realm has its own mailbox)
    const request = createLegacyRequest({
      toRealm: REALM_C,
      toPath: 'service.accepts.echo',
      fromRealm: REALM_A,
      replyTo: '_client.inbox',
      correlationId: 'a-to-c-456',
      body: { msg: 'hello from A' },
    });

    backbone.publish(getLegacyIngressTopic(REALM_C), serializeLegacyEnvelope(request));

    await new Promise(r => setTimeout(r, 500));

    assert.ok(replyFromC, 'Should receive reply from Realm C');
    assert.strictEqual((replyFromC as { ok: boolean }).ok, true);
    assert.deepStrictEqual(
      (replyFromC as { body: { echo: string } }).body,
      { echo: 'Echo from C: hello from A' }
    );

    console.log('✓ Three-realm test passed: A → C direct communication');

    gatewayA.stop();
    gatewayB.stop();
    gatewayC.stop();
  });

  it('Realm isolation: B cannot intercept messages for A', async () => {
    const REALM_A = 'secure-realm-a';
    const REALM_B = 'secure-realm-b';

    const backbone = new InMemoryFilterBroker();
    const localBusA = new InMemoryFilterBroker();
    const localBusB = new InMemoryFilterBroker();

    const gatewayA = new FederationPeer({
      localRealm: REALM_A,
      profile: WIRE_PROFILES.matrix3,
      backbone,
      localBus: localBusA,
    });

    const gatewayB = new FederationPeer({
      localRealm: REALM_B,
      profile: WIRE_PROFILES.matrix3,
      backbone,
      localBus: localBusB,
    });

    gatewayA.start();
    gatewayB.start();

    // Track what each realm's local bus receives
    let realmAReceived = 0;
    let realmBReceived = 0;

    localBusA.subscribe(`secret.accepts.data`, () => {
      realmAReceived++;
    });

    localBusB.subscribe(`secret.accepts.data`, () => {
      realmBReceived++;
    });

    // Also try subscribing B to A's internal topic (should never fire)
    localBusB.subscribe(`secret.accepts.data`, () => {
      realmBReceived += 100;  // Should never happen
    });

    await new Promise(r => setTimeout(r, 100));

    // Send message to Realm A's mailbox
    const request = createLegacyRequest({
      toRealm: REALM_A,
      toPath: 'secret.accepts.data',
      fromRealm: 'external',
      replyTo: '_inbox',
      correlationId: 'secret-msg',
      body: { secret: 'classified' },
    });

    backbone.publish(getLegacyIngressTopic(REALM_A), serializeLegacyEnvelope(request));

    await new Promise(r => setTimeout(r, 300));

    assert.strictEqual(realmAReceived, 1, 'Realm A should receive the message');
    assert.strictEqual(realmBReceived, 0, 'Realm B should NOT receive messages for Realm A');

    console.log('✓ Realm isolation verified: B cannot intercept A\'s messages');

    gatewayA.stop();
    gatewayB.stop();
  });
});
