'use strict';

const fs = require('fs');
const os = require('os');
const path = require('path');
const crypto = require('crypto');
const { execFileSync } = require('child_process');

const ROOT = path.join(__dirname, '..');
const pkg = require(path.join(ROOT, 'package.json'));
const RELEASES = path.join(ROOT, 'releases');
const DIST = path.join(ROOT, 'dist');
const zipPath = path.join(RELEASES, `ARKHON-v${pkg.version}.zip`);
const releaseRootName = `ARKHON-v${pkg.version}`;
const setupName = `ARKHON-Monitor-Setup-${pkg.version}.exe`;
const setupPath = path.join(DIST, setupName);

const forbidden = [
  /(^|\\|\/)\.env$/i,
  /(^|\\|\/)\.env\.(local|production|development|staging)$/i,
  /activation\.json$/i,
  /connector-secret\.enc$/i,
  /cloudflare.*token/i,
  /connector_secret/i,
  /CLOUDFLARE_API_TOKEN/i,
];

function assertReleaseZip() {
  if (!fs.existsSync(zipPath)) {
    throw new Error(`Release zip no encontrado: ${zipPath}. Ejecuta npm run build primero.`);
  }
}

function listZipEntries(zip) {
  const script = [
    `Add-Type -AssemblyName System.IO.Compression.FileSystem`,
    `$zip = [IO.Compression.ZipFile]::OpenRead('${zip.replace(/'/g, "''")}')`,
    `try { $zip.Entries | ForEach-Object { $_.FullName } } finally { $zip.Dispose() }`,
  ].join('; ');
  return execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script], {
    encoding: 'utf8',
    timeout: 30000,
    maxBuffer: 64 * 1024 * 1024,
  }).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
}

function assertNoForbiddenEntries(zip) {
  const entries = listZipEntries(zip);
  const blocked = entries.filter((entry) => forbidden.some((pattern) => pattern.test(entry)));
  if (blocked.length) {
    throw new Error(`Release contiene archivos no permitidos para instalador generico: ${blocked.join(', ')}`);
  }
  return entries.length;
}

function writeIexpressFiles(workDir) {
  const payloadZip = path.join(workDir, path.basename(zipPath));
  const installCmd = path.join(workDir, 'install-arkhon.cmd');
  const sed = path.join(workDir, 'arkhon-installer.sed');
  fs.copyFileSync(zipPath, payloadZip);
  fs.writeFileSync(installCmd, [
    '@echo off',
    'setlocal',
    'set "TARGET=%ProgramData%\\ARKHON\\Installer\\runtime"',
    'set "ZIP=%TARGET%\\' + path.basename(zipPath) + '"',
    'mkdir "%TARGET%" >nul 2>nul',
    `copy /Y "${path.basename(zipPath)}" "%ZIP%" >nul`,
    `powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "Expand-Archive -LiteralPath '%ZIP%' -DestinationPath '%TARGET%' -Force"`,
    'if errorlevel 1 exit /b 1',
    'where node.exe >nul 2>nul',
    'if errorlevel 1 echo Node.js runtime not found. Install ARKHON packaged runtime or Node.js before service setup. && exit /b 1',
    `set "EXTRACTED_ROOT=%TARGET%\\${releaseRootName}"`,
    'if exist "%EXTRACTED_ROOT%\\arkhon-app" (',
    '  set "ARKHON_APP_RUNTIME_SOURCE=%EXTRACTED_ROOT%\\arkhon-app"',
    '  set "ARKHON_UPDATER_RUNTIME_SOURCE=%EXTRACTED_ROOT%\\arkhon-updater"',
    ') else (',
    '  set "ARKHON_APP_RUNTIME_SOURCE=%TARGET%\\arkhon-app"',
    '  set "ARKHON_UPDATER_RUNTIME_SOURCE=%TARGET%\\arkhon-updater"',
    ')',
    'set "BILLING_PRINT_CONFIG_PATH=%ProgramData%\\ARKHON Monitor\\config"',
    'node "%ARKHON_APP_RUNTIME_SOURCE%\\scripts\\install-service-v2.js" --json',
    'if errorlevel 1 exit /b 1',
    'echo ARKHON Monitor service installed from %TARGET%',
    'endlocal',
  ].join('\r\n'), 'ascii');
  fs.writeFileSync(sed, [
    '[Version]',
    'Class=IEXPRESS',
    'SEDVersion=3',
    '[Options]',
    'PackagePurpose=InstallApp',
    'ShowInstallProgramWindow=1',
    'HideExtractAnimation=1',
    'UseLongFileName=1',
    'InsideCompressed=0',
    'CAB_FixedSize=0',
    'CAB_ResvCodeSigning=0',
    'RebootMode=N',
    'InstallPrompt=%InstallPrompt%',
    'DisplayLicense=%DisplayLicense%',
    'FinishMessage=%FinishMessage%',
    'TargetName=%TargetName%',
    'FriendlyName=%FriendlyName%',
    'AppLaunched=%AppLaunched%',
    'PostInstallCmd=%PostInstallCmd%',
    'AdminQuietInstCmd=%AdminQuietInstCmd%',
    'UserQuietInstCmd=%UserQuietInstCmd%',
    'SourceFiles=SourceFiles',
    '[Strings]',
    'InstallPrompt=',
    'DisplayLicense=',
    'FinishMessage=ARKHON Monitor package staged. Continue with service setup as Administrator.',
    `TargetName=${setupPath}`,
    `FriendlyName=ARKHON Monitor Setup ${pkg.version}`,
    'AppLaunched=install-arkhon.cmd',
    'PostInstallCmd=<None>',
    'AdminQuietInstCmd=install-arkhon.cmd',
    'UserQuietInstCmd=install-arkhon.cmd',
    'FILE0=install-arkhon.cmd',
    `FILE1=${path.basename(zipPath)}`,
    '[SourceFiles]',
    `SourceFiles0=${workDir}\\`,
    '[SourceFiles0]',
    '%FILE0%=',
    '%FILE1%=',
  ].join('\r\n'), 'ascii');
  return sed;
}

function sha256(file) {
  return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
}

function writeManifest(entryCount, reusedExisting) {
  const manifest = {
    product: 'arkhon-monitor',
    version: pkg.version,
    installer: path.basename(setupPath),
    path: setupPath,
    sha256: sha256(setupPath),
    source_zip: zipPath,
    source_zip_entries: entryCount,
    generated_at: new Date().toISOString(),
    generic: true,
    excludes_customer_secrets: true,
  };
  if (reusedExisting) manifest.reused_existing = true;
  fs.writeFileSync(path.join(DIST, 'installer-latest.json'), JSON.stringify(manifest, null, 2), 'utf8');
  console.log(`[installer] ${setupPath}`);
  console.log(`[installer] sha256=${manifest.sha256}`);
  if (reusedExisting) console.log('[installer] existente reutilizado porque Windows no permitio reemplazarlo');
}

function removeExistingInstallerOrReuse(entryCount) {
  if (!fs.existsSync(setupPath)) return false;

  try {
    fs.rmSync(setupPath, { force: true });
    return false;
  } catch (err) {
    if (err && err.code === 'EPERM') {
      const setupTime = fs.statSync(setupPath).mtimeMs;
      const zipTime = fs.statSync(zipPath).mtimeMs;
      if (setupTime >= zipTime) {
        writeManifest(entryCount, true);
        return true;
      }
    }
    throw err;
  }
}

function main() {
  assertReleaseZip();
  const entryCount = assertNoForbiddenEntries(zipPath);
  fs.mkdirSync(DIST, { recursive: true });
  if (removeExistingInstallerOrReuse(entryCount)) return;

  const iexpress = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'iexpress.exe');
  if (!fs.existsSync(iexpress)) throw new Error(`IExpress no disponible: ${iexpress}`);

  const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'arkhon-iexpress-'));
  try {
    const sed = writeIexpressFiles(workDir);
    execFileSync(iexpress, ['/N', sed], { stdio: 'pipe', timeout: 180000 });
  } finally {
    fs.rmSync(workDir, { recursive: true, force: true });
  }

  if (!fs.existsSync(setupPath)) throw new Error(`No se genero ${setupPath}`);
  writeManifest(entryCount, false);
}

main();
