import { test } from 'node:test';
import assert from 'node:assert/strict';
import { declineInvite } from './matrix';

// declineInvite must only leave when we're still in the room (invite/join/knock)
// — re-leaving an already-left remote room is what Synapse 500s on — and must
// always forget afterwards (best-effort) so the ghost invite can't linger.
const makeMx = (
  membership: string | undefined,
  opts: { leaveRejects?: boolean; forgetRejects?: boolean } = {},
) => {
  const calls: string[] = [];
  const mx = {
    getRoom: () => (membership === undefined ? null : { getMyMembership: () => membership }),
    leave: async () => {
      calls.push('leave');
      if (opts.leaveRejects) throw new Error('leave failed');
      return {};
    },
    forget: async () => {
      calls.push('forget');
      if (opts.forgetRejects) throw new Error('forget failed');
      return {};
    },
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  } as any;
  return { mx, calls };
};

test('declineInvite: invite → leave then forget', async () => {
  const { mx, calls } = makeMx('invite');
  await declineInvite(mx, '!r:s');
  assert.deepEqual(calls, ['leave', 'forget']);
});

test('declineInvite: already left → skips leave, still forgets (no double-leave 500)', async () => {
  const { mx, calls } = makeMx('leave');
  await declineInvite(mx, '!r:s');
  assert.deepEqual(calls, ['forget']);
});

test('declineInvite: join → leave then forget', async () => {
  const { mx, calls } = makeMx('join');
  await declineInvite(mx, '!r:s');
  assert.deepEqual(calls, ['leave', 'forget']);
});

test('declineInvite: no room object → only forget', async () => {
  const { mx, calls } = makeMx(undefined);
  await declineInvite(mx, '!r:s');
  assert.deepEqual(calls, ['forget']);
});

test('declineInvite: forget failure is swallowed (best-effort)', async () => {
  const { mx, calls } = makeMx('invite', { forgetRejects: true });
  await declineInvite(mx, '!r:s'); // resolves despite forget throwing
  assert.deepEqual(calls, ['leave', 'forget']);
});

test('declineInvite: genuine leave failure rejects', async () => {
  const { mx } = makeMx('invite', { leaveRejects: true });
  await assert.rejects(() => declineInvite(mx, '!r:s'), /leave failed/);
});
