import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';

import {
  clearMachineFingerprintMemoryCache,
  hashMachineRawId,
  machineWideIdPath,
  parseMacIOPlatformUUID,
  parseWindowsMachineGuid,
  parseWmicUuid,
  resolveMachineFingerprint,
} from './machineFingerprint';
import { machineFingerprintCachePath } from './paths';

void test('hashMachineRawId: стабилен и не зависит от регистра GUID', () => {
  const a = hashMachineRawId('win32', 'ABCDEF00-1111-2222-3333-444455556666');
  const b = hashMachineRawId('win32', 'abcdef00-1111-2222-3333-444455556666');
  assert.equal(a, b);
  assert.equal(a.length, 64);
  assert.notEqual(a, hashMachineRawId('linux', 'abcdef00-1111-2222-3333-444455556666'));
});

void test('parseWindowsMachineGuid', () => {
  const out = `
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography
    MachineGuid    REG_SZ    A1B2C3D4-E5F6-7890-ABCD-EF1234567890
`;
  assert.equal(parseWindowsMachineGuid(out), 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890');
  assert.equal(parseWindowsMachineGuid('nope'), null);
});

void test('parseMacIOPlatformUUID', () => {
  const out = `
+-o IOPlatformExpertDevice  <class IOPlatformExpertDevice, id 0x1000001ea, registered, matched>
  {
    "IOPlatformUUID" = "A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
  }
`;
  assert.equal(parseMacIOPlatformUUID(out), 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890');
});

void test('parseWmicUuid', () => {
  assert.equal(parseWmicUuid('UUID\nA1B2C3D4-E5F6-7890-ABCD-EF1234567890\n'), 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890');
  assert.equal(parseWmicUuid('UUID\n00000000-0000-0000-0000-000000000000\n'), null);
});

void test('resolveMachineFingerprint: override через DND_LICENSE_DEVICE_ID', () => {
  clearMachineFingerprintMemoryCache();
  const id = resolveMachineFingerprint({
    platform: 'linux',
    env: { DND_LICENSE_DEVICE_ID: 'override-device-id-12345' },
  });
  assert.equal(id, 'override-device-id-12345');
});

void test('resolveMachineFingerprint: Windows MachineGuid → одинаковый hash', () => {
  clearMachineFingerprintMemoryCache();
  const exec = () =>
    `
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography
    MachineGuid    REG_SZ    A1B2C3D4-E5F6-7890-ABCD-EF1234567890
`;
  const a = resolveMachineFingerprint({
    platform: 'win32',
    env: {},
    execFileSync: exec as never,
  });
  clearMachineFingerprintMemoryCache();
  const b = resolveMachineFingerprint({
    platform: 'win32',
    env: {},
    execFileSync: exec as never,
  });
  assert.equal(a, b);
  assert.equal(a, hashMachineRawId('win32', 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890'));
});

void test('resolveMachineFingerprint: Linux /etc/machine-id', () => {
  clearMachineFingerprintMemoryCache();
  const id = resolveMachineFingerprint({
    platform: 'linux',
    env: {},
    readFileSync: (p) => {
      if (p === '/etc/machine-id') return '0123456789abcdef0123456789abcdef\n';
      throw new Error('enoent');
    },
  });
  assert.equal(id, hashMachineRawId('linux', '0123456789abcdef0123456789abcdef'));
});

void test('resolveMachineFingerprint: fallback в machine-wide путь', () => {
  clearMachineFingerprintMemoryCache();
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'machine-fp-'));
  const env = { PROGRAMDATA: tmp };
  const p = machineWideIdPath('win32', env);
  const id1 = resolveMachineFingerprint({
    platform: 'win32',
    env,
    execFileSync: () => {
      throw new Error('no reg');
    },
  });
  clearMachineFingerprintMemoryCache();
  const id2 = resolveMachineFingerprint({
    platform: 'win32',
    env,
    execFileSync: () => {
      throw new Error('no reg');
    },
  });
  assert.equal(id1, id2);
  assert.ok(fs.existsSync(p));
  fs.rmSync(tmp, { recursive: true, force: true });
});

void test('resolveMachineFingerprint: disk cache — без повторного exec', () => {
  clearMachineFingerprintMemoryCache();
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'machine-fp-cache-'));
  const expected = hashMachineRawId('win32', 'A1B2C3D4-E5F6-7890-ABCD-EF1234567890');
  let execCalls = 0;
  const exec = () => {
    execCalls += 1;
    return `
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography
    MachineGuid    REG_SZ    A1B2C3D4-E5F6-7890-ABCD-EF1234567890
`;
  };
  const a = resolveMachineFingerprint({
    platform: 'win32',
    env: {},
    userData: tmp,
    execFileSync: exec as never,
  });
  assert.equal(a, expected);
  assert.equal(execCalls, 1);
  assert.ok(fs.existsSync(machineFingerprintCachePath(tmp)));

  clearMachineFingerprintMemoryCache();
  const b = resolveMachineFingerprint({
    platform: 'win32',
    env: {},
    userData: tmp,
    execFileSync: exec as never,
  });
  assert.equal(b, expected);
  assert.equal(execCalls, 1, 'второй вызов читает disk cache, без reg/wmic');
  fs.rmSync(tmp, { recursive: true, force: true });
});
