/**
 * 08J: Multi-Runtime Integration Test
 *
 * Tests multiple runtimes sharing a realm with shard isolation.
 */

import { describe, it, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import {
  InMemoryFilterBroker,
  ShardAllocator,
  getMxIngressTopic,
} from '@open-matrix/federation';

describe('08J: Multi-Runtime Integration', () => {
  let allocator: ShardAllocator;

  beforeEach(() => {
    allocator = new ShardAllocator();
  });

  describe('Shard Allocation', () => {
    it('allocates unique shards per realm', () => {
      const shard1 = allocator.allocate('flowpad-1');
      const shard2 = allocator.allocate('flowpad-2');
      // Different base realms, both get 00
      assert.equal(shard1, '00');
      assert.equal(shard2, '00');
    });

    it('same realm returns same shard', () => {
      const shard1 = allocator.allocate('flowpad');
      const shard2 = allocator.allocate('flowpad');
      assert.equal(shard1, shard2);
    });
  });

  describe('Ingress Topic Format', () => {
    it('generates correct ingress topic for shard', () => {
      const topic1 = getMxIngressTopic('flowpad.tab-abc', '01');
      const topic2 = getMxIngressTopic('flowpad.tab-xyz', '02');

      assert.equal(topic1, 'flowpad.tab-abc.$ingress.01');
      assert.equal(topic2, 'flowpad.tab-xyz.$ingress.02');
    });
  });

  describe('Tab Realm Isolation', () => {
    it('messages to tab-abc do not reach tab-xyz', async () => {
      const broker = new InMemoryFilterBroker();

      // Two tabs with different tab realms
      const tab1Realm = 'flowpad.tab-abc';
      const tab2Realm = 'flowpad.tab-xyz';

      const tab1Messages: string[] = [];
      const tab2Messages: string[] = [];

      // Subscribe to respective ingress
      broker.subscribe(`${tab1Realm}.$ingress.01`, (topic, msg) => {
        tab1Messages.push(msg);
      });
      broker.subscribe(`${tab2Realm}.$ingress.02`, (topic, msg) => {
        tab2Messages.push(msg);
      });

      // Send to tab1 only
      broker.publish(`${tab1Realm}.$ingress.01`, JSON.stringify({ data: 'for-tab1' }));

      // Allow async processing
      await new Promise((r) => setTimeout(r, 10));

      assert.equal(tab1Messages.length, 1, 'Tab1 should receive message');
      assert.equal(tab2Messages.length, 0, 'Tab2 should not receive message');
    });

    it('wildcard subscription receives from all shards', async () => {
      const broker = new InMemoryFilterBroker();
      const realm = 'flowpad';
      const received: string[] = [];

      // Subscribe with wildcard for all shards
      broker.subscribe(`${realm}.$ingress.+`, (topic, msg) => {
        received.push(msg);
      });

      // Send to different shards
      broker.publish(`${realm}.$ingress.01`, JSON.stringify({ shard: '01' }));
      broker.publish(`${realm}.$ingress.02`, JSON.stringify({ shard: '02' }));
      broker.publish(`${realm}.$ingress.03`, JSON.stringify({ shard: '03' }));

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

      assert.equal(received.length, 3, 'Should receive from all shards');
    });
  });

  describe('Shard-Specific Routing', () => {
    it('messages route to correct shard', async () => {
      const broker = new InMemoryFilterBroker();
      const realm = 'test-realm';

      const shard01Messages: string[] = [];
      const shard02Messages: string[] = [];

      // Subscribe to specific shards
      broker.subscribe(`${realm}.$ingress.01`, (topic, msg) => {
        shard01Messages.push(msg);
      });
      broker.subscribe(`${realm}.$ingress.02`, (topic, msg) => {
        shard02Messages.push(msg);
      });

      // Send to shard 01
      broker.publish(`${realm}.$ingress.01`, JSON.stringify({ target: '01' }));

      // Send to shard 02
      broker.publish(`${realm}.$ingress.02`, JSON.stringify({ target: '02' }));

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

      assert.equal(shard01Messages.length, 1, 'Shard 01 should receive one message');
      assert.equal(shard02Messages.length, 1, 'Shard 02 should receive one message');

      // Verify correct routing
      assert.ok(shard01Messages[0].includes('"target":"01"'), 'Shard 01 got correct message');
      assert.ok(shard02Messages[0].includes('"target":"02"'), 'Shard 02 got correct message');
    });
  });
});
