/**
 * XLL load-failure diagnostics for Windows.
 *
 * Collects system state (MOTW, PE architecture, registry keys, XLL log, trust settings)
 * when the ShortcutXL add-in fails to load in Excel. The collected data is written
 * to session JSONL for remote debugging via GCS traces.
 */
import { closeSync, existsSync, openSync, readSync, statSync } from 'fs';
import { resolve } from 'path';
import { getXllPath } from '../../config.js';
import { escapePSString, run, runPS } from '../install-utils.js';
import { note, setCurrentStep } from './utils.js';
const DIAGNOSTIC_TIMEOUT_MS = 5_000;
const MAX_LOG_BYTES = 64 * 1024;
function safeRun(command, timeout = DIAGNOSTIC_TIMEOUT_MS) {
    try {
        return run(command, { timeout });
    }
    catch (error) {
        return {
            ok: false,
            stdout: '',
            stderr: error instanceof Error ? error.message : String(error)
        };
    }
}
function safeRunPS(script, timeout = DIAGNOSTIC_TIMEOUT_MS) {
    try {
        return runPS(script, { timeout });
    }
    catch (error) {
        return {
            ok: false,
            stdout: '',
            stderr: error instanceof Error ? error.message : String(error)
        };
    }
}
function formatResult(result) {
    if (result.ok) {
        const output = result.stdout.trim();
        return output || 'ok';
    }
    return `check failed: ${result.stderr || 'unknown error'}`;
}
/** Read the last ~10 lines of a log file. The first line may be truncated if the seek lands mid-line. */
function readLogTail(path) {
    let fd;
    try {
        const size = statSync(path).size;
        const start = Math.max(0, size - MAX_LOG_BYTES);
        const length = size - start;
        if (length <= 0)
            return '';
        const buffer = Buffer.alloc(length);
        fd = openSync(path, 'r');
        readSync(fd, buffer, 0, length, start);
        return buffer.toString('utf-8').split('\n').slice(-10).join('\n');
    }
    catch {
        return `${path}: exists but unreadable`;
    }
    finally {
        if (fd !== undefined) {
            try {
                closeSync(fd);
            }
            catch {
                // Ignore cleanup failures in diagnostics.
            }
        }
    }
}
function collectMotw(xllPath) {
    return formatResult(safeRunPS(`if (Get-Item -LiteralPath '${escapePSString(xllPath)}' -Stream Zone.Identifier -ErrorAction SilentlyContinue) { Write-Output 'BLOCKED' } else { Write-Output 'UNBLOCKED' }`));
}
function collectXllArch(xllPath) {
    return formatResult(safeRunPS(`
      try {
        $bytes = [System.IO.File]::ReadAllBytes('${escapePSString(xllPath)}')
        $peOffset = [BitConverter]::ToInt32($bytes, 60)
        $machine = [BitConverter]::ToUInt16($bytes, $peOffset + 4)
        switch ($machine) {
          0x14c  { Write-Output 'x86 (32-bit)' }
          0x8664 { Write-Output 'x64 (64-bit)' }
          default { Write-Output ('unknown: 0x{0:X4}' -f $machine) }
        }
      } catch { Write-Output ('error: ' + $_.Exception.Message) }
    `));
}
function collectExcelArch() {
    return formatResult(safeRunPS(`
      $excelProc = Get-Process -Name EXCEL -ErrorAction SilentlyContinue | Select-Object -First 1
      if ($excelProc) {
        try {
          $modules = $excelProc.Modules | Select-Object -First 1
          $path = $modules.FileName
          $bytes = [System.IO.File]::ReadAllBytes($path)
          $peOffset = [BitConverter]::ToInt32($bytes, 60)
          $machine = [BitConverter]::ToUInt16($bytes, $peOffset + 4)
          switch ($machine) {
            0x14c  { Write-Output 'x86 (32-bit)' }
            0x8664 { Write-Output 'x64 (64-bit)' }
            default { Write-Output ('unknown: 0x{0:X4}' -f $machine) }
          }
        } catch { Write-Output ('error reading PE: ' + $_.Exception.Message) }
      } else { Write-Output 'Excel not running' }
    `));
}
function collectRegistryOpenKeys() {
    return formatResult(safeRunPS(`
      $versions = @('16.0', '15.0', '14.0')
      foreach ($ver in $versions) {
        $optionsKey = "HKCU:\\Software\\Microsoft\\Office\\$ver\\Excel\\Options"
        if (Test-Path $optionsKey) {
          Write-Output "Office $ver Options key exists"
          $props = Get-ItemProperty -Path $optionsKey -ErrorAction SilentlyContinue
          if ($props) {
            foreach ($p in $props.PSObject.Properties) {
              if ($p.Name -match '^OPEN') {
                Write-Output ("  " + $p.Name + " = " + $p.Value)
              }
            }
          }
          break
        }
      }
    `));
}
function collectXllLog() {
    const logPaths = [
        `C:\\Users\\${process.env.USERNAME ?? 'unknown'}\\AppData\\Local\\Temp\\shortcutxl.log`,
        `${process.env.TEMP ?? ''}\\shortcutxl.log`
    ];
    for (const path of logPaths) {
        if (path && existsSync(path)) {
            return readLogTail(path);
        }
    }
    return 'not found (XLL never started initializing)';
}
function collectTrustSecurity() {
    return formatResult(safeRunPS(`
      $versions = @('16.0', '15.0', '14.0')
      foreach ($ver in $versions) {
        $secKey = "HKCU:\\Software\\Microsoft\\Office\\$ver\\Excel\\Security"
        if (Test-Path $secKey) {
          $props = Get-ItemProperty -Path $secKey -ErrorAction SilentlyContinue
          if ($props.VBAWarnings) { Write-Output "VBAWarnings=$($props.VBAWarnings)" }
          if ($props.BlockContentExecutionFromInternet) { Write-Output "BlockContentFromInternet=$($props.BlockContentExecutionFromInternet)" }
        }
        $trustKey = "HKCU:\\Software\\Microsoft\\Office\\$ver\\Excel\\Security\\Trusted Locations"
        if (Test-Path $trustKey) { Write-Output "TrustedLocations key exists" }
        $disabledKey = "HKCU:\\Software\\Microsoft\\Office\\$ver\\Excel\\Resiliency\\DisabledItems"
        if (Test-Path $disabledKey) {
          $items = Get-ItemProperty -Path $disabledKey -ErrorAction SilentlyContinue
          $count = ($items.PSObject.Properties | Where-Object { $_.Name -notmatch '^PS' }).Count
          Write-Output "DisabledItems: $count entries"
          foreach ($p in $items.PSObject.Properties) {
            if ($p.Name -notmatch '^PS') { Write-Output ("  " + $p.Name + " = " + $p.Value) }
          }
        }
        break
      }
    `));
}
function writeDiagnosticNote(diagnostics) {
    note([
        '--- XLL Load Failure Diagnostics ---',
        `XLL exists: ${diagnostics.xllExists} (${diagnostics.xllPath})`,
        `MOTW: ${diagnostics.motw}`,
        `XLL arch: ${diagnostics.xllArch}`,
        `Excel arch: ${diagnostics.excelArch}`,
        `Registry: ${diagnostics.registryOpenKeys}`,
        `Excel running: ${diagnostics.excelRunning}`,
        `XLL log: ${diagnostics.xllLog}`,
        `Trust/Security: ${diagnostics.trustSecurity}`
    ].join('\n'));
}
/** Collect all XLL diagnostics and write a summary to the preflight log. */
export function collectPreflightDiagnostics() {
    setCurrentStep('diagnostics');
    const xllPath = resolve(getXllPath()).replace(/\//g, '\\');
    // Every collector uses safeRun/safeRunPS which never throw,
    // so this construction is safe without a top-level try/catch.
    const diagnostics = {
        xllExists: existsSync(xllPath),
        xllPath,
        motw: collectMotw(xllPath),
        xllArch: collectXllArch(xllPath),
        excelArch: collectExcelArch(),
        registryOpenKeys: collectRegistryOpenKeys(),
        excelRunning: safeRun('tasklist /FI "IMAGENAME eq EXCEL.EXE" /NH').stdout.includes('EXCEL.EXE'),
        xllLog: collectXllLog(),
        trustSecurity: collectTrustSecurity()
    };
    writeDiagnosticNote(diagnostics);
    return diagnostics;
}
//# sourceMappingURL=diagnostics.js.map