import './setup.mjs';
import test from 'node:test';
import assert from 'node:assert/strict';
import { load } from './setup.mjs';

const { parseIntunewin } = await load('../services/intunewinParser.js');

// --- Minimal stored-method (no compression) ZIP builder for fixtures --------

function crc32(buf) {
  let crc = 0xffffffff;
  for (let i = 0; i < buf.length; i++) {
    crc ^= buf[i];
    for (let j = 0; j < 8; j++) crc = (crc >>> 1) ^ (0xedb88320 & -(crc & 1));
  }
  return (crc ^ 0xffffffff) >>> 0;
}

function makeStoredZip(entries) {
  const locals = [];
  const centrals = [];
  let offset = 0;
  for (const { name, data } of entries) {
    const nameBuf = Buffer.from(name, 'utf8');
    const crc = crc32(data);
    const local = Buffer.alloc(30);
    local.writeUInt32LE(0x04034b50, 0);
    local.writeUInt16LE(20, 4);
    local.writeUInt16LE(0, 8); // method = stored
    local.writeUInt32LE(crc, 14);
    local.writeUInt32LE(data.length, 18);
    local.writeUInt32LE(data.length, 22);
    local.writeUInt16LE(nameBuf.length, 26);
    const localRecord = Buffer.concat([local, nameBuf, data]);

    const central = Buffer.alloc(46);
    central.writeUInt32LE(0x02014b50, 0);
    central.writeUInt16LE(20, 4);
    central.writeUInt16LE(20, 6);
    central.writeUInt16LE(0, 10); // method = stored
    central.writeUInt32LE(crc, 16);
    central.writeUInt32LE(data.length, 20);
    central.writeUInt32LE(data.length, 24);
    central.writeUInt16LE(nameBuf.length, 28);
    central.writeUInt32LE(offset, 42);
    centrals.push(Buffer.concat([central, nameBuf]));

    locals.push(localRecord);
    offset += localRecord.length;
  }
  const cd = Buffer.concat(centrals);
  const localsBuf = Buffer.concat(locals);
  const eocd = Buffer.alloc(22);
  eocd.writeUInt32LE(0x06054b50, 0);
  eocd.writeUInt16LE(entries.length, 8);
  eocd.writeUInt16LE(entries.length, 10);
  eocd.writeUInt32LE(cd.length, 12);
  eocd.writeUInt32LE(localsBuf.length, 16);
  return Buffer.concat([localsBuf, cd, eocd]);
}

const detectionXml = `<?xml version="1.0" encoding="utf-8"?>
<ApplicationInfo ToolVersion="1.8.6">
  <Name>Contoso VPN</Name>
  <UnencryptedContentSize>2048</UnencryptedContentSize>
  <FileName>IntunePackage.intunewin</FileName>
  <SetupFile>ContosoVpnSetup.exe</SetupFile>
  <EncryptionInfo>
    <EncryptionKey>QUJDRA==</EncryptionKey>
    <MacKey>RUZHSA==</MacKey>
    <InitializationVector>SUpLTA==</InitializationVector>
    <Mac>TU5PUA==</Mac>
    <ProfileIdentifier>ProfileVersion1</ProfileIdentifier>
    <FileDigest>UVJTVA==</FileDigest>
    <FileDigestAlgorithm>SHA256</FileDigestAlgorithm>
  </EncryptionInfo>
</ApplicationInfo>`;

const encryptedPayload = Buffer.from('this-stands-in-for-the-encrypted-intunewin-bytes');

const fixture = makeStoredZip([
  { name: 'IntuneWinPackage/Metadata/Detection.xml', data: Buffer.from(detectionXml, 'utf8') },
  { name: 'IntuneWinPackage/Contents/IntunePackage.intunewin', data: encryptedPayload }
]);

test('parses setup file, sizes, and encrypted payload', () => {
  const parsed = parseIntunewin(fixture);
  assert.equal(parsed.setupFile, 'ContosoVpnSetup.exe');
  assert.equal(parsed.unencryptedContentSize, 2048);
  assert.equal(parsed.encryptedContentSize, encryptedPayload.length);
  assert.ok(parsed.encryptedContent.equals(encryptedPayload));
});

test('maps EncryptionInfo to Graph fileEncryptionInfo', () => {
  const { fileEncryptionInfo } = parseIntunewin(fixture);
  assert.equal(fileEncryptionInfo.encryptionKey, 'QUJDRA==');
  assert.equal(fileEncryptionInfo.macKey, 'RUZHSA==');
  assert.equal(fileEncryptionInfo.initializationVector, 'SUpLTA==');
  assert.equal(fileEncryptionInfo.mac, 'TU5PUA==');
  assert.equal(fileEncryptionInfo.profileIdentifier, 'ProfileVersion1');
  assert.equal(fileEncryptionInfo.fileDigest, 'UVJTVA==');
  assert.equal(fileEncryptionInfo.fileDigestAlgorithm, 'SHA256');
});

test('rejects a non-.intunewin buffer', () => {
  assert.throws(() => parseIntunewin(Buffer.from('not a zip at all')));
});
