import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
  isServerName,
  getMxIdServer,
  getMxIdLocalPart,
  isUserId,
  isRoomId,
  isRoomAlias,
  knockSupported,
  restrictedSupported,
  knockRestrictedSupported,
  creatorsSupported,
} from './matrix';

test('isServerName matches domain-like strings', () => {
  assert.equal(isServerName('matrix.org'), true);
  assert.equal(isServerName('a.b.example.com'), true);
  assert.equal(isServerName('my-server.io'), true);
  // The regex looks for a domain substring anywhere, so embedded domains match.
  assert.equal(isServerName('user@matrix.org'), true);
  assert.equal(isServerName('matrix.org:8448'), true);
});

test('isServerName rejects strings without a dotted TLD', () => {
  assert.equal(isServerName('localhost'), false);
  assert.equal(isServerName(''), false);
  assert.equal(isServerName('nodot'), false);
  // TLD must be at least two letters.
  assert.equal(isServerName('a.b'), false);
  // Numeric "TLD" is not matched by the [a-zA-Z]{2,} part.
  assert.equal(isServerName('127.0.0.1'), false);
});

test('getMxIdServer extracts the server portion after the colon', () => {
  assert.equal(getMxIdServer('@alice:matrix.org'), 'matrix.org');
  assert.equal(getMxIdServer('#room:example.com'), 'example.com');
  assert.equal(getMxIdServer('+group:server.io'), 'server.io');
  assert.equal(getMxIdServer('$event:host.net'), 'host.net');
});

test('getMxIdServer returns undefined for ids that do not match', () => {
  // '!' is not an accepted sigil in the matcher.
  assert.equal(getMxIdServer('!roomid:matrix.org'), undefined);
  assert.equal(getMxIdServer('alice:matrix.org'), undefined);
  assert.equal(getMxIdServer('@alice'), undefined);
  assert.equal(getMxIdServer(''), undefined);
  // A space in the local part breaks the [^\s:]+ group.
  assert.equal(getMxIdServer('@al ice:matrix.org'), undefined);
});

test('getMxIdLocalPart extracts the local part between sigil and colon', () => {
  assert.equal(getMxIdLocalPart('@alice:matrix.org'), 'alice');
  assert.equal(getMxIdLocalPart('#general:example.com'), 'general');
  assert.equal(getMxIdLocalPart('+group:server.io'), 'group');
  assert.equal(getMxIdLocalPart('$event:host.net'), 'event');
});

test('getMxIdLocalPart returns undefined for non-matching ids', () => {
  assert.equal(getMxIdLocalPart('!roomid:matrix.org'), undefined);
  assert.equal(getMxIdLocalPart('alice'), undefined);
  assert.equal(getMxIdLocalPart('@alice'), undefined);
  assert.equal(getMxIdLocalPart(''), undefined);
});

test('isUserId requires a valid mxid starting with @', () => {
  assert.equal(isUserId('@alice:matrix.org'), true);
  assert.equal(isUserId('@bob:example.com'), true);
  // Valid mxid shape but wrong sigil.
  assert.equal(isUserId('#room:matrix.org'), false);
  assert.equal(isUserId('+group:matrix.org'), false);
  // Starts with @ but has no server (invalid mxid).
  assert.equal(isUserId('@alice'), false);
  assert.equal(isUserId('alice:matrix.org'), false);
  assert.equal(isUserId(''), false);
});

test('isRoomId only checks the leading ! sigil', () => {
  assert.equal(isRoomId('!abc:matrix.org'), true);
  // No validity check is performed, so a bare '!' passes.
  assert.equal(isRoomId('!'), true);
  assert.equal(isRoomId('!anything-at-all'), true);
  assert.equal(isRoomId('@alice:matrix.org'), false);
  assert.equal(isRoomId('#room:matrix.org'), false);
  assert.equal(isRoomId(''), false);
});

test('isRoomAlias requires a valid mxid starting with #', () => {
  assert.equal(isRoomAlias('#general:matrix.org'), true);
  assert.equal(isRoomAlias('#room:example.com'), true);
  // Wrong sigils.
  assert.equal(isRoomAlias('@alice:matrix.org'), false);
  assert.equal(isRoomAlias('!room:matrix.org'), false);
  // Starts with # but missing server part.
  assert.equal(isRoomAlias('#general'), false);
  assert.equal(isRoomAlias(''), false);
});

test('knockSupported gates room versions 1-6 out', () => {
  ['1', '2', '3', '4', '5', '6'].forEach((v) => {
    assert.equal(knockSupported(v), false, `version ${v}`);
  });
  assert.equal(knockSupported('7'), true);
  assert.equal(knockSupported('8'), true);
  assert.equal(knockSupported('10'), true);
  // Unknown / non-numeric versions are treated as supported.
  assert.equal(knockSupported('org.matrix.msc'), true);
});

test('restrictedSupported gates room versions 1-7 out', () => {
  ['1', '2', '3', '4', '5', '6', '7'].forEach((v) => {
    assert.equal(restrictedSupported(v), false, `version ${v}`);
  });
  assert.equal(restrictedSupported('8'), true);
  assert.equal(restrictedSupported('9'), true);
});

test('knockRestrictedSupported gates room versions 1-9 out', () => {
  ['1', '2', '3', '4', '5', '6', '7', '8', '9'].forEach((v) => {
    assert.equal(knockRestrictedSupported(v), false, `version ${v}`);
  });
  assert.equal(knockRestrictedSupported('10'), true);
  assert.equal(knockRestrictedSupported('11'), true);
});

test('creatorsSupported gates room versions 1-11 out', () => {
  ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'].forEach((v) => {
    assert.equal(creatorsSupported(v), false, `version ${v}`);
  });
  assert.equal(creatorsSupported('12'), true);
  assert.equal(creatorsSupported('13'), true);
});
