import assert from 'node:assert/strict';
import { generateKeyPairSync, sign } from 'node:crypto';
import test from 'node:test';

import { canonicalJson } from '../../shared/license/canonicalJson';
import { joinSignedLicenseToken } from '../../shared/license/tokenFormat';

import { verifyLicenseToken } from './verifyLicenseToken';

void test('verifyLicenseToken: валидная подпись и срок', () => {
  const { publicKey, privateKey } = generateKeyPairSync('ed25519');
  const pubB64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
  const licensePayload = {
    v: 1 as const,
    sub: 'lic_test_1',
    pid: 'dnd_player',
    iat: 100,
    exp: 2_000_000_000,
    did: null as string | null,
  };
  const body = canonicalJson(licensePayload);
  const sig = sign(null, Buffer.from(body, 'utf8'), privateKey);
  const token = joinSignedLicenseToken(body, new Uint8Array(sig.buffer, sig.byteOffset, sig.byteLength));

  const ok = verifyLicenseToken(token, {
    nowSec: 1_700_000_000,
    deviceId: 'any',
    publicKeyOverrideSpkiDerB64: pubB64,
  });
  if (!ok.ok) assert.fail('expected ok');
  assert.equal(ok.payload.sub, 'lic_test_1');
});

void test('verifyLicenseToken: неверное устройство', () => {
  const { publicKey, privateKey } = generateKeyPairSync('ed25519');
  const pubB64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
  const payload = {
    v: 1 as const,
    sub: 'lic_test_2',
    pid: 'dnd_player',
    iat: 100,
    exp: 2_000_000_000,
    did: 'device-a',
  };
  const body = canonicalJson(payload);
  const sig = sign(null, Buffer.from(body, 'utf8'), privateKey);
  const token = joinSignedLicenseToken(body, new Uint8Array(sig.buffer, sig.byteOffset, sig.byteLength));

  const bad = verifyLicenseToken(token, {
    nowSec: 1_700_000_000,
    deviceId: 'device-b',
    publicKeyOverrideSpkiDerB64: pubB64,
  });
  if (bad.ok) assert.fail('expected failure');
  assert.equal(bad.reason, 'wrong_device');
});

void test('verifyLicenseToken: принимает legacy deviceId при миграции', () => {
  const { publicKey, privateKey } = generateKeyPairSync('ed25519');
  const pubB64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
  const payload = {
    v: 1 as const,
    sub: 'lic_legacy',
    pid: 'dnd_player',
    iat: 100,
    exp: 2_000_000_000,
    did: 'legacy-uuid-from-userdata',
  };
  const body = canonicalJson(payload);
  const sig = sign(null, Buffer.from(body, 'utf8'), privateKey);
  const token = joinSignedLicenseToken(body, new Uint8Array(sig.buffer, sig.byteOffset, sig.byteLength));

  const ok = verifyLicenseToken(token, {
    nowSec: 1_700_000_000,
    deviceId: 'machine-fingerprint-hash',
    alsoAcceptDeviceIds: ['legacy-uuid-from-userdata'],
    publicKeyOverrideSpkiDerB64: pubB64,
  });
  if (!ok.ok) assert.fail(`expected ok, got ${ok.reason}`);
});

void test('verifyLicenseToken: токен с переносами строк после копирования', () => {
  const { publicKey, privateKey } = generateKeyPairSync('ed25519');
  const pubB64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
  const licensePayload = {
    v: 1 as const,
    sub: 'lic_wrap',
    pid: 'dnd_player',
    iat: 100,
    exp: 2_000_000_000,
    did: null as string | null,
  };
  const body = canonicalJson(licensePayload);
  const sig = sign(null, Buffer.from(body, 'utf8'), privateKey);
  const token = joinSignedLicenseToken(body, new Uint8Array(sig.buffer, sig.byteOffset, sig.byteLength));
  const mid = Math.max(1, Math.floor(token.length / 2));
  const messy = `${token.slice(0, mid)}\n${token.slice(mid, mid + 3)} \r\n ${token.slice(mid + 3)}`;

  const ok = verifyLicenseToken(messy, {
    nowSec: 1_700_000_000,
    deviceId: 'any',
    publicKeyOverrideSpkiDerB64: pubB64,
  });
  if (!ok.ok) assert.fail(`expected ok, got ${ok.reason}`);
  assert.equal(ok.payload.sub, 'lic_wrap');
});
