import { describe, test, expect } from 'vitest';
import { relayFormWiring } from './form-relay-wiring';

describe('relayFormWiring deterministic + unique fid (thread node id, no Math.random)', () => {
  test('no recipient: no relay, no marker, no fid needed', () => {
    const wiring = relayFormWiring(undefined, undefined, '/legacy', 'node-1');
    expect(wiring.useRelay).toBe(false);
    expect(wiring.marker).toBe('');
  });

  test('same node id -> identical marker + placeholder ids across two calls (deterministic)', () => {
    const w1 = relayFormWiring('a@b.com', '/thx', undefined, 'node-form1');
    const w2 = relayFormWiring('a@b.com', '/thx', undefined, 'node-form1');
    expect(w1.marker).toBe(w2.marker);
    expect(w1.actionAttr).toBe(w2.actionAttr);
  });

  test('marker id and placeholder id always match each other', () => {
    const w = relayFormWiring('a@b.com', '/thx', undefined, 'node-form1');
    const mid = w.marker.match(/id="([^"]+)"/)![1];
    expect(w.actionAttr).toBe(`__WHP_FORM_ACTION__${mid}__`);
  });

  test('different node ids -> different fids (no collision)', () => {
    const w1 = relayFormWiring('a@b.com', '/thx', undefined, 'node-form1');
    const w2 = relayFormWiring('a@b.com', '/thx', undefined, 'node-form2');
    const mid1 = w1.marker.match(/id="([^"]+)"/)![1];
    const mid2 = w2.marker.match(/id="([^"]+)"/)![1];
    expect(mid1).not.toBe(mid2);
  });

  test('no nodeId (legacy call): still deterministic across repeated calls, not random', () => {
    const w1 = relayFormWiring('a@b.com', '/thx', undefined);
    const w2 = relayFormWiring('a@b.com', '/thx', undefined);
    expect(w1.marker).toBe(w2.marker);
    expect(w1.actionAttr).toBe(w2.actionAttr);
  });
});

/* ---------- Webhook destination (Task 10) ---------- */

/** The publish-side parser, verbatim from web-files/libs/FormRelayRewrite.php's
 *  fs_rewrite_contact_forms() -- ported to JS so a drift in the emitter's
 *  attribute ORDER, NAMES or QUOTING fails here rather than at publish time,
 *  where it either leaks the recipient address or refuses the publish. */
const PUBLISH_MARKER_RE =
  /^<!--WHP-FORM id="([^"]+)"((?: [a-z]+="[^"]*")*) recipient="([^"]*)" thankyou="([^"]*)"-->$/;

describe('relayFormWiring email destination stays byte-identical to the legacy shape', () => {
  test('no destination arg, type undefined, and type "email" all produce the same marker', () => {
    const legacy = relayFormWiring('a@b.com', '/thx', '/act', 'n1');
    const undef = relayFormWiring('a@b.com', '/thx', '/act', 'n1', {});
    const email = relayFormWiring('a@b.com', '/thx', '/act', 'n1', { type: 'email', url: 'https://x.example/y', secretId: 'whs_1_abc', authMode: 'bearer' });
    expect(undef.marker).toBe(legacy.marker);
    expect(email.marker).toBe(legacy.marker);
    expect(email.actionAttr).toBe(legacy.actionAttr);
    expect(legacy.marker).toMatch(/^<!--WHP-FORM id="F_[0-9a-z]+" recipient="a@b\.com" thankyou="\/thx"-->$/);
  });

  test('an email form with no recipient is still not a relay at all', () => {
    const w = relayFormWiring('', '/thx', '/legacy', 'n1', { type: 'email' });
    expect(w.useRelay).toBe(false);
    expect(w.marker).toBe('');
  });
});

describe('relayFormWiring webhook marker matches the publish-side parser exactly', () => {
  const w = relayFormWiring('fb@b.com', '/thx', '#', 'n1', {
    type: 'webhook', url: 'https://hooks.example.com/x', secretId: 'whs_7_abc123', authMode: 'bearer',
  });

  test('the whole marker parses with FormRelayRewrite.php\'s pattern', () => {
    const m = w.marker.match(PUBLISH_MARKER_RE);
    expect(m).not.toBeNull();
    expect(m![3]).toBe('fb@b.com');
    expect(m![4]).toBe('/thx');
  });

  test('the optional attributes sit BETWEEN id and recipient, lowercase-named', () => {
    const attrs = w.marker.match(PUBLISH_MARKER_RE)![2];
    expect(attrs).toBe(' type="webhook" url="https://hooks.example.com/x" secret="whs_7_abc123" authmode="bearer"');
  });

  test('a webhook with no recipient still emits a relay marker', () => {
    const bare = relayFormWiring('', '', '#', 'n1', { type: 'webhook', url: 'https://hooks.example.com/x' });
    expect(bare.useRelay).toBe(true);
    expect(bare.marker).toMatch(PUBLISH_MARKER_RE);
    expect(bare.marker).toContain('authmode="signature"');
  });

  test('two webhook forms with no node id and different urls get different fids', () => {
    const a = relayFormWiring('', '', '#', undefined, { type: 'webhook', url: 'https://a.example/x' });
    const b = relayFormWiring('', '', '#', undefined, { type: 'webhook', url: 'https://b.example/x' });
    expect(a.marker).not.toBe(b.marker);
  });
});

describe('relayFormWiring escapes EVERY marker attribute value', () => {
  /* The property the publish-time strip is built on: no raw `"`, `<`, `>` or a
     literal `-->` may reach a marker attribute value. An unescaped one truncates
     the strip mid-marker and ships the recipient address in the page source, or
     trips the post-condition and refuses the publish outright. */
  const hostile = 'a"b<c>d\'e-->f';
  const w = relayFormWiring(`${hostile}@x.com`, hostile, '#', 'n1', {
    type: 'webhook', url: `https://x/${hostile}`, secretId: hostile, authMode: 'bearer',
  });

  test('the marker still parses as ONE marker (nothing escaped out of a value)', () => {
    expect(w.marker).toMatch(PUBLISH_MARKER_RE);
  });

  test.each([
    ['url', `https://x/${hostile}`],
    ['secret', hostile],
    ['recipient', `${hostile}@x.com`],
    ['thankyou', hostile],
  ])('%s carries no raw ", <, > or -->', (name) => {
    const value = w.marker.match(new RegExp(` ${name}="([^"]*)"`))![1];
    expect(value).not.toMatch(/["<>]/);
    expect(value).not.toContain('-->');
    expect(value).toContain('&quot;');
    expect(value).toContain('&lt;');
    expect(value).toContain('&gt;');
  });

  test('the raw hostile string never appears anywhere in the marker', () => {
    expect(w.marker).not.toContain(hostile);
  });
});

describe('relayFormWiring allowlists type and authmode instead of trusting them', () => {
  test('type is matched case-insensitively -- "Webhook" is a webhook, not a silent email', () => {
    const w = relayFormWiring('a@b.com', '', '#', 'n1', { type: 'WebHook', url: 'https://x/y' });
    expect(w.marker).toContain('type="webhook"');
  });

  test('an unknown type falls back to the legacy email marker', () => {
    const w = relayFormWiring('a@b.com', '', '#', 'n1', { type: 'slack', url: 'https://x/y' } as any);
    expect(w.marker).not.toContain('type=');
    expect(w.marker).toBe(relayFormWiring('a@b.com', '', '#', 'n1').marker);
  });

  test('a hostile authMode collapses to signature and cannot break out of the attribute', () => {
    const w = relayFormWiring('a@b.com', '', '#', 'n1', {
      type: 'webhook', url: 'https://x/y', authMode: 'bearer" onx="1',
    });
    expect(w.marker).toContain('authmode="signature"');
    expect(w.marker).toMatch(PUBLISH_MARKER_RE);
  });

  test('"Bearer" is promoted to the exact literal the relay compares against', () => {
    const w = relayFormWiring('a@b.com', '', '#', 'n1', { type: 'webhook', url: 'https://x/y', authMode: ' Bearer ' });
    expect(w.marker).toContain('authmode="bearer"');
  });
});
