import { existsSync, openSync, readSync, closeSync } from "node:fs";

export const PUBLISHED_BUILD_PLATFORM = "linux";
export const PUBLISHED_BUILD_ARCH = "x64";

const HEADER_SIZE = 4096;
const MAX_FAT_ARCH_COUNT = 30;

function mapElfMachine(machine) {
  switch (machine) {
    case 62:
      return "x64";
    case 183:
      return "arm64";
    default:
      return null;
  }
}

function mapMachCpuType(cpuType) {
  switch (cpuType) {
    case 0x01000007:
      return "x64";
    case 0x0100000c:
      return "arm64";
    default:
      return null;
  }
}

function mapPeMachine(machine) {
  switch (machine) {
    case 0x8664:
      return "x64";
    case 0xaa64:
      return "arm64";
    default:
      return null;
  }
}

function readUInt16(buffer, offset, littleEndian) {
  return littleEndian ? buffer.readUInt16LE(offset) : buffer.readUInt16BE(offset);
}

function readUInt32(buffer, offset, littleEndian) {
  return littleEndian ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset);
}

const ELF_MAGIC = 0x7f454c46;

function detectElfTarget(buffer) {
  if (buffer.length < 20) return null;
  if (buffer.readUInt32BE(0) !== ELF_MAGIC) return null;

  const littleEndian = buffer[5] !== 2;
  const arch = mapElfMachine(readUInt16(buffer, 18, littleEndian));
  if (!arch) return null;

  return { platform: "linux", architectures: [arch] };
}

const THIN_MACH_MAGIC = new Map([
  [0xfeedface, false],
  [0xfeedfacf, false],
  [0xcefaedfe, true],
  [0xcffaedfe, true],
]);
const FAT_MACH_MAGIC = new Map([
  [0xcafebabe, false],
  [0xcafebabf, false],
  [0xbebafeca, true],
  [0xbfbafeca, true],
]);

function detectMachTarget(buffer) {
  if (buffer.length < 8) return null;

  const magic = buffer.readUInt32BE(0);

  if (THIN_MACH_MAGIC.has(magic)) {
    const littleEndian = THIN_MACH_MAGIC.get(magic);
    const arch = mapMachCpuType(readUInt32(buffer, 4, littleEndian));
    if (!arch) return null;
    return { platform: "darwin", architectures: [arch] };
  }

  if (!FAT_MACH_MAGIC.has(magic)) return null;

  const littleEndian = FAT_MACH_MAGIC.get(magic);
  const isFat64 = magic === 0xcafebabf || magic === 0xbfbafeca;
  const archCount = readUInt32(buffer, 4, littleEndian);
  if (archCount > MAX_FAT_ARCH_COUNT) return null;
  const entrySize = isFat64 ? 32 : 20;
  const architectures = new Set();

  for (let index = 0; index < archCount; index += 1) {
    const offset = 8 + index * entrySize;
    if (offset + 4 > buffer.length) break;
    const arch = mapMachCpuType(readUInt32(buffer, offset, littleEndian));
    if (arch) architectures.add(arch);
  }

  if (architectures.size === 0) return null;
  return { platform: "darwin", architectures: [...architectures] };
}

function detectPeTarget(buffer) {
  if (buffer.length < 0x40) return null;
  if (buffer.readUInt16LE(0) !== 0x5a4d) return null;

  const peHeaderOffset = buffer.readUInt32LE(0x3c);
  if (peHeaderOffset + 6 > buffer.length) return null;
  if (buffer.readUInt32LE(peHeaderOffset) !== 0x00004550) return null;

  const arch = mapPeMachine(buffer.readUInt16LE(peHeaderOffset + 4));
  if (!arch) return null;
  return { platform: "win32", architectures: [arch] };
}

export function detectNativeBinaryTarget(buffer) {
  return detectElfTarget(buffer) ?? detectMachTarget(buffer) ?? detectPeTarget(buffer);
}

export function readNativeBinaryTarget(binaryPath) {
  if (!existsSync(binaryPath)) return null;

  let fd;
  try {
    fd = openSync(binaryPath, "r");
    const buffer = Buffer.alloc(HEADER_SIZE);
    const bytesRead = readSync(fd, buffer, 0, HEADER_SIZE, 0);
    return detectNativeBinaryTarget(buffer.subarray(0, bytesRead));
  } catch (err) {
    console.warn(`  ⚠️  Could not read native binary at ${binaryPath}: ${err.message}`);
    return null;
  } finally {
    if (fd !== undefined) closeSync(fd);
  }
}

export function isNativeBinaryCompatible(
  binaryPath,
  { runtimePlatform = process.platform, runtimeArch = process.arch, dlopen = process.dlopen } = {}
) {
  const target = readNativeBinaryTarget(binaryPath);

  if (target) {
    if (target.platform !== runtimePlatform || !target.architectures.includes(runtimeArch)) {
      return false;
    }
  } else if (runtimePlatform !== PUBLISHED_BUILD_PLATFORM || runtimeArch !== PUBLISHED_BUILD_ARCH) {
    return false;
  }

  try {
    dlopen({ exports: {} }, binaryPath);
    return true;
  } catch (err) {
    console.warn(`  ⚠️  Native binary dlopen failed: ${err.message}`);
    return false;
  }
}
