/**
 * routes/dotnet.js
 * Detect installed .NET Framework + .NET Core/5+ versions.
 * Install / upgrade via Chocolatey (async, frontend polls status).
 * Mount at /api/dotnet in server.js.
 */
const express      = require('express');
const { execSync, exec } = require('child_process');
const router       = express.Router();
const authMw       = require('../middleware/auth');
const { writeAudit } = require('./audit');

const ps = (cmd, timeout = 30000) =>
  execSync(`powershell -ExecutionPolicy Bypass -Command "${cmd}"`, { timeout }).toString().trim();

// ── .NET Framework versions from registry ─────────────────
function getFrameworkVersions() {
  try {
    const raw = ps(`
      $path = 'HKLM:\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP'
      $results = @()

      # v1 - v3.5
      Get-ChildItem $path -ErrorAction SilentlyContinue | ForEach-Object {
        $key = $_.Name.Split('\\')[-1]
        if ($key -match '^v\\d') {
          $ver  = $_.GetValue('Version')
          $sp   = $_.GetValue('SP')
          $inst = $_.GetValue('Install')
          if ($inst -eq 1 -and $ver) {
            $results += [PSCustomObject]@{
              version = $ver
              sp      = if($sp){ "SP$sp" } else { '' }
              key     = $key
              type    = 'Framework'
            }
          }
          # v4 sub-keys (Full, Client)
          Get-ChildItem $_.PSPath -ErrorAction SilentlyContinue | ForEach-Object {
            $sub  = $_
            $ver2 = $sub.GetValue('Version')
            $sp2  = $sub.GetValue('SP')
            $inst2= $sub.GetValue('Install')
            $rel  = $sub.GetValue('Release')
            if ($inst2 -eq 1 -and $ver2) {
              $friendly = switch ($rel) {
                378389 { '4.5' } 378675 { '4.5.1' } 379893 { '4.5.2' }
                393295 { '4.6' } 393297 { '4.6' }   394254 { '4.6.1' }
                394271 { '4.6.1' } 394802 { '4.6.2' } 394806 { '4.6.2' }
                460798 { '4.7' }   460805 { '4.7' }   461308 { '4.7.1' }
                461310 { '4.7.1' } 461808 { '4.7.2' } 461814 { '4.7.2' }
                528040 { '4.8' }   528049 { '4.8' }   528372 { '4.8' }
                528449 { '4.8' }   533320 { '4.8.1' } 533325 { '4.8.1' }
                default { $ver2 }
              }
              $results += [PSCustomObject]@{
                version = $friendly
                sp      = if($sp2){ "SP$sp2" } else { '' }
                key     = "$key\$($sub.Name.Split('\\')[-1])"
                type    = 'Framework'
              }
            }
          }
        }
      }
      $results | Sort-Object version -Descending | ConvertTo-Json -Compress
    `);
    if (!raw) return [];
    const list = JSON.parse(raw);
    return Array.isArray(list) ? list : [list];
  } catch { return []; }
}

// ── .NET Core / .NET 5+ runtimes & SDKs ──────────────────
function getDotnetCoreVersions() {
  const result = { sdks: [], runtimes: [], dotnetPath: null };
  try {
    // Find dotnet.exe
    const dotnetPath = ps(`(Get-Command dotnet -ErrorAction SilentlyContinue).Source`).trim();
    if (!dotnetPath) return result;
    result.dotnetPath = dotnetPath;

    // SDKs
    try {
      const sdkRaw = execSync('dotnet --list-sdks', { timeout: 10000 }).toString().trim();
      result.sdks = sdkRaw.split('\n').map(l => l.trim()).filter(Boolean).map(l => {
        const match = l.match(/^([\d.]+)\s+\[(.+)\]$/);
        return match ? { version: match[1], path: match[2] } : { version: l, path: '' };
      });
    } catch {}

    // Runtimes
    try {
      const rtRaw = execSync('dotnet --list-runtimes', { timeout: 10000 }).toString().trim();
      result.runtimes = rtRaw.split('\n').map(l => l.trim()).filter(Boolean).map(l => {
        const match = l.match(/^(\S+)\s+([\d.]+)\s+\[(.+)\]$/);
        return match ? { name: match[1], version: match[2], path: match[3] } : { name: l, version: '', path: '' };
      });
    } catch {}
  } catch {}
  return result;
}

// ── Available installable versions (curated list) ─────────
const INSTALLABLE = [
  // .NET (Core/5+) SDKs
  { id: 'dotnet-9.0', label: '.NET 9.0 SDK', type: 'dotnet',     choco: 'dotnet-9.0-sdk', latest: true  },
  { id: 'dotnet-8.0', label: '.NET 8.0 SDK (LTS)', type: 'dotnet', choco: 'dotnet-sdk',  latest: false  },
  { id: 'dotnet-7.0', label: '.NET 7.0 SDK', type: 'dotnet',     choco: 'dotnet-7.0-sdk', latest: false },
  { id: 'dotnet-6.0', label: '.NET 6.0 SDK (LTS)', type: 'dotnet', choco: 'dotnet-sdk --version 6.0.427', latest: false },
  // ASP.NET Core Runtimes
  { id: 'aspnet-9.0', label: 'ASP.NET Core 9.0 Runtime', type: 'aspnet', choco: 'dotnet-9.0-windowshosting', latest: true },
  { id: 'aspnet-8.0', label: 'ASP.NET Core 8.0 Runtime', type: 'aspnet', choco: 'dotnet-windowshosting',     latest: false },
  // .NET Framework
  { id: 'netfx-481',  label: '.NET Framework 4.8.1',     type: 'framework', choco: 'dotnetfx --version 4.8.1', latest: true },
  { id: 'netfx-48',   label: '.NET Framework 4.8',       type: 'framework', choco: 'dotnetfx --version 4.8.0', latest: false },
];

// ── Track ongoing installs ─────────────────────────────────
const _installStatus = {}; // id -> { status, startedAt, log }

// ── GET /api/dotnet — full status ──────────────────────────
router.get('/', authMw, (req, res) => {
  const framework = getFrameworkVersions();
  const core      = getDotnetCoreVersions();
  res.json({
    framework,
    sdks:           core.sdks,
    runtimes:       core.runtimes,
    dotnetPath:     core.dotnetPath,
    installable:    INSTALLABLE,
    installStatus:  _installStatus
  });
});

// ── GET /api/dotnet/status/:id — poll install progress ─────
router.get('/status/:id', authMw, (req, res) => {
  res.json(_installStatus[req.params.id] || { status: 'idle' });
});

// ── POST /api/dotnet/install — start async install ─────────
router.post('/install', authMw, (req, res) => {
  const { id } = req.body;
  const pkg = INSTALLABLE.find(p => p.id === id);
  if (!pkg) return res.status(400).json({ error: 'Unknown package' });

  if (_installStatus[id] && _installStatus[id].status === 'running')
    return res.status(400).json({ error: 'Install already in progress' });

  _installStatus[id] = { status: 'running', startedAt: new Date().toISOString(), log: 'Starting installation...' };
  res.json({ ok: true, message: `Installing ${pkg.label} in background. Poll /api/dotnet/status/${id} for progress.` });

  const chocoInstall = [
    `if(!(Get-Command choco -ErrorAction SilentlyContinue)){`,
    `  Set-ExecutionPolicy Bypass -Scope Process -Force;`,
    `  [Net.ServicePointManager]::SecurityProtocol=[Net.ServicePointManager]::SecurityProtocol -bor 3072;`,
    `  iex((New-Object Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))`,
    `}`,
    `choco install ${pkg.choco} -y --no-progress`
  ].join(' ');

  exec(
    `powershell -ExecutionPolicy Bypass -Command "${chocoInstall}"`,
    { timeout: 20 * 60 * 1000 },
    (err, stdout, stderr) => {
      if (err) {
        _installStatus[id] = { status: 'error', log: (stderr || err.message).substring(0, 500) };
        console.error(`[dotnet/install/${id}] failed:`, err.message);
      } else {
        _installStatus[id] = { status: 'done', log: 'Installation completed successfully.' };
        writeAudit(req, 'dotnet.install', `Installed ${pkg.label}`);
        console.log(`[dotnet/install/${id}] done`);
      }
    }
  );
});

// ── POST /api/dotnet/uninstall — remove via choco ──────────
router.post('/uninstall', authMw, (req, res) => {
  const { id } = req.body;
  const pkg = INSTALLABLE.find(p => p.id === id);
  if (!pkg) return res.status(400).json({ error: 'Unknown package' });

  res.json({ ok: true, message: `Uninstalling ${pkg.label} in background.` });
  const chocoId = pkg.choco.split(' ')[0]; // strip version flags for uninstall
  exec(
    `powershell -ExecutionPolicy Bypass -Command "choco uninstall ${chocoId} -y --no-progress"`,
    { timeout: 10 * 60 * 1000 },
    (err) => {
      if (err) console.error(`[dotnet/uninstall/${id}]`, err.message);
      else { writeAudit(req, 'dotnet.uninstall', `Uninstalled ${pkg.label}`); }
    }
  );
});

module.exports = router;