/**
 * Windows pre-flight setup for ShortcutXL.
 *
 * Steps: Git Bash → pip packages → Chrome → XLL registry → launch Excel → smoke test.
 * All Windows-specific: PowerShell, registry, COM, XLL, embedded Python.
 */
import chalk from 'chalk';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join, resolve } from 'path';
import { resetShellConfig } from '../../app/tools/bash-runtime.js';
import { getEffectiveExcelHttpUrl } from '../../app/xll/endpoint.js';
import { clearXllSessionCache, readXllSessionError, xllFetch } from '../../app/xll/session.js';
import { getEmbeddedPythonExe, getStableXllDir, getXllPath, VERSION } from '../../config.js';
import { escapePSString, run, runPS } from '../install-utils.js';
import { header, log, note, ok, promptConfirm, promptEnter, refreshPath, runWithProgress, SPINNER_FRAMES } from './utils.js';
// ── Pip packages (embedded Python) ──────────────────────────────────────
export async function ensurePipPackages() {
    header('Python Packages', 'Calculation Engine');
    const pythonExe = getEmbeddedPythonExe();
    if (!existsSync(pythonExe)) {
        log('Shortcut agent will set up Python.', 'Will finish setup automatically.');
        return;
    }
    // Verify embedded Python + pip work (both are shipped in the npm package)
    const versionCheck = run(`"${pythonExe}" --version`);
    if (versionCheck.ok) {
        ok(versionCheck.stdout, 'Ready.');
    }
    else {
        log('Shortcut agent will configure Python.', 'Will finish setup automatically.');
        return;
    }
    const pipCheck = run(`"${pythonExe}" -m pip --version`);
    if (!pipCheck.ok) {
        log('Shortcut agent will configure pip.', 'Will finish setup automatically.');
        return;
    }
    // Install remaining packages. pip, openpyxl, httpx, and pywin32 are
    // pre-installed in the embedded distribution by vendor-python.ps1.
    // Playwright is installed here on first run.
    // A version stamp avoids re-running pip on every launch after a sync.
    const pipStampFile = join(getStableXllDir(), '.pip-version');
    let pipUpToDate = false;
    try {
        pipUpToDate =
            existsSync(pipStampFile) && readFileSync(pipStampFile, 'utf-8').trim() === VERSION;
    }
    catch {
        /* proceed with install */
    }
    if (pipUpToDate) {
        ok('Packages already installed.', 'Ready.');
        return;
    }
    log('Installing required packages...', 'Installing components...');
    const pipResult = await runWithProgress(`"${pythonExe}" -m pip install playwright --no-warn-script-location`, { timeout: 300_000, label: 'Setting up' });
    if (pipResult.ok) {
        ok('Packages installed.', 'Ready.');
        try {
            writeFileSync(pipStampFile, VERSION, 'utf-8');
        }
        catch {
            /* non-fatal */
        }
    }
    else {
        log('Shortcut agent will finish installing packages.', 'Will finish setup automatically.');
    }
}
// ── Chrome ──────────────────────────────────────────────────────────────
export async function ensureChromeWindows() {
    header('Chrome');
    const hasSystemChrome = detectChromeWindows();
    if (hasSystemChrome) {
        ok('Chrome found — SEC filing support ready.');
        return;
    }
    // If the embedded Python isn't available, we can't install Chrome via
    // playwright. Log it and let the installation agent handle it later.
    const pythonExe = getEmbeddedPythonExe();
    if (!existsSync(pythonExe)) {
        log('Shortcut agent will set up Chrome.', 'Will finish setup automatically.');
        return;
    }
    const installChrome = await promptConfirm('Install Chrome? (Used for SEC filing PDF conversion)');
    if (installChrome) {
        log('Installing Chrome for PDF conversion...', 'Installing...');
        const pwResult = await runWithProgress(`"${pythonExe}" -m playwright install chrome`, {
            timeout: 300_000,
            label: 'Installing'
        });
        if (pwResult.ok) {
            ok('Browser installed.', 'Ready.');
        }
        else {
            log('Shortcut agent will finish setting up Chrome.', 'Will finish setup automatically.');
        }
    }
    else {
        ok('Skipped — can be installed later if needed.');
    }
}
function detectChromeWindows() {
    const chromeCheck = runPS(`
    $path = (Get-ItemProperty -Path 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe' -ErrorAction SilentlyContinue).'(default)'
    if ($path -and (Test-Path $path)) { Write-Output 'FOUND' } else { Write-Output 'MISSING' }
  `);
    return chromeCheck.ok && chromeCheck.stdout.includes('FOUND');
}
// ── Git Bash ────────────────────────────────────────────────────────────
export async function ensureGitBash() {
    header('Git', 'System Tools');
    // Use git's bundled bash directly — plain `bash` can hang on Windows
    // when WSL is installed but not configured.
    const bashCheck = run('git --version', { timeout: 5_000 });
    if (bashCheck.ok) {
        ok('Installed.', 'Ready.');
        return;
    }
    log('Git not found.', 'Installing required tools...');
    const confirmed = await promptConfirm('Install a required system tool? (Git for Windows)');
    if (!confirmed) {
        log('Shortcut agent will set up Git.', 'Will finish setup automatically.');
        return;
    }
    const wingetCheck = run('winget --version');
    if (!wingetCheck.ok) {
        log('Installing package manager...', 'Preparing installer...');
        run('powershell -Command "Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"', { timeout: 60_000 });
        const retry = run('winget --version');
        if (!retry.ok) {
            log('Shortcut agent will set up the package manager.', 'Will finish setup automatically.');
            return;
        }
    }
    log('Installing Git (you may see a permissions prompt — click Yes)...', 'Installing (you may see a permissions prompt — click Yes)...');
    let result = await runWithProgress('winget install --id Git.Git -e -h --source winget --accept-source-agreements --accept-package-agreements', { timeout: 300_000, label: 'Installing' });
    if (!result.ok && !result.stdout.includes('already installed')) {
        // Machine-wide install failed (UAC denied / no admin) — retry per-user
        log('Retrying as per-user install...', 'Retrying...');
        result = await runWithProgress('winget install --id Git.Git -e -h --scope user --source winget --accept-source-agreements --accept-package-agreements', { timeout: 300_000, label: 'Installing' });
    }
    if (!result.ok && !result.stdout.includes('already installed')) {
        log('Shortcut agent will finish installing Git.', 'Will finish setup automatically.');
        return;
    }
    refreshPath();
    resetShellConfig();
    const gitCheck = run('git --version');
    if (!gitCheck.ok) {
        log('Git installed — Shortcut agent will verify the setup.', 'Almost ready.');
        return;
    }
    ok('Installed.', 'Ready.');
}
// ── XLL registry ────────────────────────────────────────────────────────
export function ensureXllRegistry() {
    header('Excel Add-in', 'Excel Integration');
    const xllPath = resolve(getXllPath()).replace(/\//g, '\\');
    if (!existsSync(xllPath)) {
        log('Shortcut agent will set up the Excel add-in.', 'Will finish setup automatically.');
        return;
    }
    // Find Office version and write the OPEN key so Excel loads the XLL on startup.
    // Creates the Options key if it doesn't exist yet (fresh Office installs).
    const safeXllPath = escapePSString(xllPath);
    const result = runPS(`
    $ErrorActionPreference = 'Stop'
    $xllValue = '/R "${safeXllPath}"'
    $versions = @('16.0', '15.0', '14.0')
    $officeVer = $null

    foreach ($ver in $versions) {
      $excelKey = "HKCU:\\Software\\Microsoft\\Office\\$ver\\Excel"
      if (Test-Path $excelKey) { $officeVer = $ver; break }
    }

    if (-not $officeVer) {
      Write-Output "NO_OFFICE"
      exit 0
    }

    $optionsKey = "HKCU:\\Software\\Microsoft\\Office\\$officeVer\\Excel\\Options"

    if (-not (Test-Path $optionsKey)) {
      New-Item -Path $optionsKey -Force | Out-Null
    }

    # Check if already registered
    $props = Get-ItemProperty -Path $optionsKey -ErrorAction SilentlyContinue
    if ($props) {
      foreach ($p in $props.PSObject.Properties) {
        if ($p.Name -match '^OPEN' -and $p.Value -match 'ShortcutXL') {
          Write-Output ('ALREADY:' + $officeVer)
          exit 0
        }
      }
    }

    # Find next available OPEN slot
    $openKeys = @()
    if ($props) {
      $openKeys = @($props.PSObject.Properties | Where-Object { $_.Name -match '^OPEN' } | ForEach-Object { $_.Name })
    }
    if ($openKeys.Count -eq 0) {
      $slot = 'OPEN'
    } else {
      $nums = @($openKeys | ForEach-Object { if ($_ -eq 'OPEN') { 0 } else { [int]($_ -replace 'OPEN','') } })
      $next = ($nums | Measure-Object -Maximum).Maximum + 1
      $slot = "OPEN$next"
    }

    New-ItemProperty -Path $optionsKey -Name $slot -Value $xllValue -PropertyType String -Force | Out-Null

    # Verify the write persisted
    $check = Get-ItemProperty -Path $optionsKey -Name $slot -ErrorAction SilentlyContinue
    if ($check -and $check.$slot -match 'ShortcutXL') {
      Write-Output ('REGISTERED:' + $officeVer + ':' + $slot)
    } else {
      Write-Output ('WRITE_FAILED:' + $officeVer + ':' + $slot)
    }
  `);
    if (result.stdout.includes('ALREADY:')) {
        ok('Already registered.', 'Ready.');
    }
    else if (result.stdout.includes('REGISTERED:')) {
        ok('Registered with Excel.', 'Configured.');
    }
    else if (result.stdout.includes('WRITE_FAILED:')) {
        log('Registry key was written but not found on re-read. The write may not have persisted.', 'Shortcut agent will verify the add-in registration.');
    }
    else if (result.stdout.includes('NO_OFFICE')) {
        log('Shortcut agent will configure Excel.', 'Will finish setup automatically.');
    }
    else {
        log('Shortcut agent will finish add-in registration.', 'Will finish setup automatically.');
        note(`Registry script failed. ok=${result.ok}, stdout=${result.stdout}, stderr=${result.stderr}`);
    }
}
// ── Excel launch + XLL verification ─────────────────────────────────────
export async function ensureExcelAndXll() {
    header('Launch Excel', 'Connect to Excel');
    // Check if the real Excel -> XLL -> Python -> COM path is already reachable.
    if (await tryExecProbe(2_000)) {
        ok('Excel is running with ShortcutXL.');
        return;
    }
    // Check if Excel is running without XLL
    const tasklist = run('tasklist /FI "IMAGENAME eq EXCEL.EXE" /NH');
    const excelRunning = tasklist.ok && tasklist.stdout.includes('EXCEL.EXE');
    clearXllSessionCache(); // New Excel instance = new bearer token
    if (excelRunning) {
        await promptEnter('Please close Excel, reopen it (any workbook), then press Enter.');
    }
    else {
        await promptEnter('Please open Excel (any workbook) then press Enter.');
    }
    log('Checking for ShortcutXL...');
    for (let i = 0; i < 15; i++) {
        await new Promise((r) => setTimeout(r, 1000));
        process.stderr.write(`\r  ${chalk.cyan(SPINNER_FRAMES[i % SPINNER_FRAMES.length])} Waiting for ShortcutXL... (${i + 1}s)\x1b[K`);
        if (await tryExecProbe(2_000)) {
            process.stderr.write('\r\x1b[K');
            ok('ShortcutXL is ready.');
            return;
        }
    }
    process.stderr.write('\r\x1b[K');
    log('Shortcut agent will finish connecting to Excel.', 'Will retry automatically.');
}
async function tryExecProbe(timeoutMs) {
    try {
        const res = await xllFetch(`${getEffectiveExcelHttpUrl()}/exec`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                code: 'print(f"Excel {app.Version}")'
            }),
            signal: AbortSignal.timeout(timeoutMs)
        });
        if (!res.ok)
            return false;
        const data = (await res.json());
        return !data.error;
    }
    catch {
        return false;
    }
}
// ── Smoke test ──────────────────────────────────────────────────────────
export async function smokeTestExcel() {
    header('Connection Test', 'Verifying Setup');
    // Check if the XLL reported an init-time error (e.g. Python DLL missing)
    const xllError = readXllSessionError();
    if (xllError) {
        log(`XLL initialization error: ${xllError}`, 'XLL failed to initialize.');
        return false;
    }
    log('Verifying Excel connection...', 'Almost done...');
    // Retry a few times — Excel's COM subsystem (HWND hierarchy + ROT entry)
    // can take a moment to become available after the XLL HTTP server is up.
    const MAX_ATTEMPTS = 3;
    for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
        try {
            const res = await xllFetch(`${getEffectiveExcelHttpUrl()}/exec`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    code: 'print(f"Excel {app.Version}")'
                }),
                signal: AbortSignal.timeout(10_000)
            });
            if (!res.ok) {
                if (attempt < MAX_ATTEMPTS) {
                    await new Promise((r) => setTimeout(r, 2000));
                    continue;
                }
                log('Shortcut agent will troubleshoot the connection.', 'Will retry automatically.');
                note(`HTTP status ${res.status}`);
                return false;
            }
            const data = (await res.json());
            if (data.error) {
                if (attempt < MAX_ATTEMPTS) {
                    log(`Waiting for Excel COM... (attempt ${attempt}/${MAX_ATTEMPTS})`, 'Connecting...');
                    await new Promise((r) => setTimeout(r, 2000));
                    continue;
                }
                log('Shortcut agent will troubleshoot Excel.', 'Will retry automatically.');
                note(data.error);
                return false;
            }
            ok(data.output?.trim() ?? 'Connected', 'Connected to Excel!');
            return true;
        }
        catch (e) {
            if (attempt < MAX_ATTEMPTS) {
                await new Promise((r) => setTimeout(r, 2000));
                continue;
            }
            log('Shortcut agent will troubleshoot the connection.', 'Will retry automatically.');
            note(e instanceof Error ? e.message : String(e));
            return false;
        }
    }
    return false;
}
//# sourceMappingURL=windows.js.map