import test from 'node:test';
import assert from 'node:assert/strict';
import { InMemoryExactBroker, InMemoryFilterBroker } from '@open-matrix/federation/transport/inmem';
import { FederationPeer, ExactInstanceResolver } from '@open-matrix/federation/federation_peer';
import { getInternalTopic, getLegacyIngressTopic } from '@open-matrix/federation/topics';
import { createLegacyRequest, deserializeTaggedEnvelope, serializeLegacyEnvelope } from '@open-matrix/federation/envelope_codec';
import { parseMatrixUri } from '@open-matrix/federation/uri';
import { isRecord } from '../../src/util/guards';
import type { Transport } from '@open-matrix/federation/transport/base';

test('matrix3 compat: legacy request with ttlMs + flat replyTo is accepted and produces a reply', async () => {
  const backbone: Transport = new InMemoryExactBroker();
  const localBus = new InMemoryFilterBroker();

  const serverRealm = 'matrix-3';
  const clientRealm = 'flowpad-demo.tab-01';

  // Start EdgeGateway (server-side)
  const edge = new FederationPeer({
    localRealm: serverRealm,
    backbone,
    localBus,
  });
  edge.start();

  // Local handler (no helper wrappers) that replies with a NON-standard reply payload
  // to prove lenient decoding on the EdgeGateway.
  const toPath = 'project/daemon.project.accepts.scan';
  const internalTopic = getInternalTopic(serverRealm, toPath);
  localBus.subscribe(internalTopic, (_t, raw) => {
    const parsed = (() => {
      try {
        return JSON.parse(raw) as unknown;
      } catch {
        return null;
      }
    })();
    if (!isRecord(parsed)) throw new Error('internal request must be a JSON object');
    const replyTo = parsed.replyTo;
    if (typeof replyTo !== 'string') throw new Error('replyTo must be a string');

    // Reply with `{ result: ... }` only (no correlationId, no ok), to exercise lenient reply parsing.
    localBus.publish(replyTo, JSON.stringify({ result: { hello: true } }));
  });

  // Client mailbox watcher
  const clientMailbox = getLegacyIngressTopic(clientRealm);
  const serverMailbox = getLegacyIngressTopic(serverRealm);

  const correlationId = 'cid-ttlms-001';
  const replyToPath = `_replies.emits.${correlationId}`;

  const repPromise = new Promise<unknown>((resolve, reject) => {
    const timer = setTimeout(() => reject(new Error('timeout waiting for reply')), 250);
    const unsub = backbone.subscribe(clientMailbox, (_topic, payload) => {
      clearTimeout(timer);
      unsub();
      const tagged = deserializeTaggedEnvelope(payload);
      if (!tagged) throw new Error('expected an envelope');
      if (tagged.profile !== 'matrix3') throw new Error('expected matrix3 envelope');
      if (tagged.kind !== 'reply') throw new Error('expected matrix3 reply envelope');
      resolve(tagged.envelope.body);
    });
  });

  // Send raw legacy request with ttlMs (expiry semantics) and flat replyTo
  const req = createLegacyRequest({
    toRealm: serverRealm,
    toPath,
    fromRealm: clientRealm,
    replyTo: replyToPath,
    correlationId,
    body: { depth: 1 },
    ttlMs: 1234,
    type: 'request',
  });

  backbone.publish(serverMailbox, serializeLegacyEnvelope(req));

  const body = await repPromise;
  assert.deepEqual(body, { hello: true });
});

test('matrix3 compat: reply detection works for ok:true and ok:false', () => {
  const okRep = JSON.stringify({
    toRealm: 'a',
    toPath: '_replies.emits.x',
    fromRealm: 'b',
    correlationId: 'x',
    ok: true,
    body: { v: 1 },
  });
  const badRep = JSON.stringify({
    toRealm: 'a',
    toPath: '_replies.emits.x',
    fromRealm: 'b',
    correlationId: 'x',
    ok: false,
    body: null,
    error: { code: 'MX.FAIL', message: 'nope' },
  });

  const t1 = deserializeTaggedEnvelope(okRep);
  if (!t1) throw new Error('expected non-null');
  if (t1.profile !== 'matrix3') throw new Error('expected matrix3 profile');
  if (t1.kind !== 'reply') throw new Error('expected reply');

  const t2 = deserializeTaggedEnvelope(badRep);
  if (!t2) throw new Error('expected non-null');
  if (t2.profile !== 'matrix3') throw new Error('expected matrix3 profile');
  if (t2.kind !== 'reply') throw new Error('expected reply');
});

test('matrix3 compat: URI parsing tolerant (legacy vs /-/ delimiter) canonicalizes to same toPath', () => {
  const a = parseMatrixUri('matrix-3/project/daemon.project.accepts.scan');
  const b = parseMatrixUri('matrix-3/project/-/daemon/project/accepts/scan');
  if (!a) throw new Error('expected non-null');
  if (!b) throw new Error('expected non-null');
  assert.equal(a.realm, 'matrix-3');
  assert.equal(b.realm, 'matrix-3');
  assert.equal(a.toPath, b.toPath);
});
