import { spawn } from 'node:child_process';
import fs from 'node:fs';
import { config } from '../config.js';
import { nameWithoutExtensionAny, path } from '../utils/pathUtils.js';

// Wraps a PSADT/package source folder into a `.intunewin` using the Microsoft
// Win32 Content Prep Tool (IntuneWinAppUtil.exe) on Windows, or an optional
// cross-platform packager via INTUNEWIN_BUILD_COMMAND. The command construction
// is pure and unit-tested; the spawn wrapper is thin.

export function resolveToolPath() {
  return config.intunewinUtilPath || path.join(config.toolsDir, 'IntuneWinAppUtil.exe');
}

// Where to put the tool and whether we can build here.
export function builderAvailability() {
  if (config.intunewinBuildCommand) {
    return { available: true, mode: 'external-command', toolPath: null };
  }
  const toolPath = resolveToolPath();
  if (!fs.existsSync(toolPath)) {
    return { available: false, mode: 'bundled', toolPath, reason: `IntuneWinAppUtil.exe not found at ${toolPath}. Place it in the tools directory or set INTUNEWIN_UTIL_PATH / INTUNEWIN_BUILD_COMMAND.` };
  }
  if (process.platform !== 'win32') {
    return { available: false, mode: 'bundled', toolPath, reason: 'IntuneWinAppUtil.exe requires Windows. Set INTUNEWIN_BUILD_COMMAND to use a cross-platform packager.' };
  }
  return { available: true, mode: 'bundled', toolPath };
}

// Pure: produce the spawn descriptor for the configured packager.
export function buildPackagerInvocation({ toolPath, sourceFolder, setupFile, outputDir, commandTemplate }) {
  if (commandTemplate) {
    const command = commandTemplate
      .split('{source}').join(sourceFolder)
      .split('{setup}').join(setupFile)
      .split('{output}').join(outputDir);
    return { shell: true, command };
  }
  return {
    shell: false,
    command: toolPath,
    args: ['-c', sourceFolder, '-s', setupFile, '-o', outputDir, '-q']
  };
}

// IntuneWinAppUtil names the output after the setup file's base name.
export function expectedOutputFile(outputDir, setupFile) {
  return path.join(outputDir, `${nameWithoutExtensionAny(setupFile, 'package')}.intunewin`);
}

export function buildIntuneWin({ sourceFolder, setupFile, outputDir }) {
  return new Promise((resolve, reject) => {
    const availability = builderAvailability();
    if (!availability.available) return reject(new Error(availability.reason || 'Packager not available'));
    if (!sourceFolder || !fs.existsSync(sourceFolder)) return reject(new Error(`Source folder not found: ${sourceFolder}`));
    if (!setupFile) return reject(new Error('A setup file (entry installer) is required'));
    fs.mkdirSync(outputDir, { recursive: true });

    const invocation = buildPackagerInvocation({
      toolPath: availability.toolPath,
      sourceFolder,
      setupFile,
      outputDir,
      commandTemplate: config.intunewinBuildCommand
    });

    const child = invocation.shell
      ? spawn(invocation.command, { shell: true })
      : spawn(invocation.command, invocation.args, { shell: false });

    let stderr = '';
    child.stderr.on('data', (chunk) => { stderr += String(chunk); });
    child.on('error', (error) => reject(new Error(`Packager failed to start: ${error.message}`)));
    child.on('close', (code) => {
      const outputFile = expectedOutputFile(outputDir, setupFile);
      if (code === 0 && fs.existsSync(outputFile)) return resolve({ outputFile });
      reject(new Error(`Packager exited with code ${code}${stderr ? `: ${stderr.trim()}` : ''}`));
    });
  });
}
