/**
 * routes/upgrade-verifications.js — Post-upgrade verification ingestion + operator UI.
 *
 * Owns: POST /api/upgrade-verifications  — agent posts verification bundle after self-upgrade
 *       GET  /api/upgrade-verifications/connection/:id — latest verification for a connection
 *       GET  /api/upgrade-verifications/recent         — last 20 rows for /status/installer
 *       POST /api/connections/:id/re-verify            — trigger a fresh verification probe
 * Does NOT own: audit row lifecycle (db/agent-upgrade-audit.js), installer_validation_runs
 *               greenfield/EBS rows (routes/installer-validation.js), upgrade dispatch (routes/agent.js).
 */

'use strict';

const express = require('express');
const crypto  = require('crypto');
const verificationsDb = require('../db/upgrade-verifications');
const upgradeAuditDb  = require('../db/agent-upgrade-audit');
const agentDb         = require('../db/agent');
const { requireAuth, requireAdmin } = require('../middleware/auth');
const { encrypt, decrypt } = require('../crypto-utils');
const channel = require('../services/agent-channel');

const router = express.Router();

const APP_URL        = process.env.APP_URL || 'https://tunevault.app';
const POLSIA_API_KEY = process.env.POLSIA_API_KEY;
const EMAIL_API_URL  = 'https://polsia.com/api/proxy/email';
const ALERT_EMAIL    = process.env.INSTALLER_ALERT_EMAIL || process.env.ADMIN_EMAILS?.split(',')[0]?.trim();

// Grace window for previous key after rotation (matches routes/agent.js)
const GRACE_WINDOW_MS = 5 * 60 * 1000;

// ── Agent API key verification ────────────────────────────────────────────────
// Replicates the verifyApiKey logic from routes/agent.js so this module is self-contained.

async function verifyApiKey(apiKey, connectionId) {
  const conn = await agentDb.getConnectionKeyState(connectionId);
  if (!conn) return null;

  const currentKey = decrypt(conn.proxy_api_key_enc);
  if (currentKey === apiKey) return conn;

  if (conn.proxy_api_key_enc_previous && conn.key_rotated_at) {
    const elapsed = Date.now() - new Date(conn.key_rotated_at).getTime();
    if (elapsed < GRACE_WINDOW_MS) {
      const prevKey = decrypt(conn.proxy_api_key_enc_previous);
      if (prevKey === apiKey) return conn;
    }
  }

  return null;
}

// ── POST /api/upgrade-verifications ──────────────────────────────────────────
// Called by the agent after self-upgrade + 3 heartbeats confirm stability.
//
// Body: {
//   connection_id: number,
//   api_key: string,          — the agent's current TUNEVAULT_API_KEY
//   verification_id: string,  — UUID generated by agent
//   from_version: string,
//   to_version: string,
//   checks_passed: number,
//   checks_total: number,
//   api_test_status: number,  — HTTP status code from POST /api/test (expected 410)
//   probe_7: 'pass'|'fail',
//   probe_8: 'pass'|'fail',
//   error_detail: string|null — populated if any probe failed
// }

router.post('/upgrade-verifications', async (req, res) => {
  const {
    connection_id, api_key,
    verification_id,
    from_version, to_version,
    checks_passed, checks_total,
    api_test_status,
    probe_7, probe_8,
    error_detail,
  } = req.body;

  if (!connection_id || !api_key) {
    return res.status(400).json({ error: 'connection_id and api_key required' });
  }

  // Authenticate the agent
  const conn = await verifyApiKey(api_key, connection_id).catch(() => null);
  if (!conn) {
    return res.status(401).json({ error: 'Invalid API key or connection' });
  }

  // Derive overall pass/fail
  // App servers run ~10 EBS checks — not 54 Oracle DB checks. Lower threshold accordingly.
  const isAppsServer = conn.server_type === 'apps' || conn.server_type === 'both';
  const minChecks    = isAppsServer ? 7 : 54;
  const checksOk    = checks_total > 0 && checks_passed >= minChecks;
  const apiTestOk   = api_test_status === 410;
  const probe7Ok    = probe_7 === 'pass';
  const probe8Ok    = probe_8 === 'pass';
  const allPass     = checksOk && apiTestOk && probe7Ok && probe8Ok;
  const status      = allPass ? 'pass' : 'fail';

  const payload = {
    verification_id: verification_id || crypto.randomUUID(),
    checks_passed:   checks_passed || 0,
    checks_total:    checks_total  || 0,
    api_test_status: api_test_status || null,
    probe_7:         probe_7 || 'fail',
    probe_8:         probe_8 || 'fail',
    error_detail:    error_detail || null,
    timestamp:       new Date().toISOString(),
  };

  try {
    // 1. Persist to audit row
    await verificationsDb.saveVerification({
      connectionId: connection_id,
      payload,
      status,
    });

    // 2. Insert into installer_validation_runs for /status/installer display
    const runId = verification_id || `post-upgrade-${connection_id}-${Date.now()}`;
    await verificationsDb.insertPostUpgradeValidationRun({
      connectionId:  connection_id,
      connectionName: conn.name,
      fromVersion:   from_version,
      toVersion:     to_version,
      runId,
      agentVersion:  to_version,
      checksPassed:  checks_passed || 0,
      checksTotal:   checks_total  || 0,
      probe7Status:  probe_7 || 'fail',
      probe8Status:  probe_8 || 'fail',
      overallOutcome: status,
      durationMs:    null,
    });

    // 3. If failed — mark connection degraded + alert operator (fire-and-forget)
    if (!allPass) {
      verificationsDb.markConnectionUpgradeDegraded(connection_id)
        .catch(err => console.error('[upgrade-verif] degraded mark error:', err.message));

      sendDegradedAlert({
        connectionId:  connection_id,
        connectionName: conn.name,
        fromVersion:   from_version,
        toVersion:     to_version,
        payload,
        checksOk, apiTestOk, probe7Ok, probe8Ok,
      }).catch(err => console.error('[upgrade-verif] alert email error:', err.message));
    } else {
      // Verification passed — clear any previous degraded state
      verificationsDb.clearUpgradeDegradedStatus(connection_id)
        .catch(err => console.error('[upgrade-verif] clear degraded error:', err.message));
    }

    res.json({ ok: true, status, verification_id: payload.verification_id });
  } catch (err) {
    console.error('[upgrade-verif] save error:', err.message);
    res.status(500).json({ error: 'Failed to save verification result' });
  }
});

// ── GET /api/upgrade-verifications/recent ────────────────────────────────────
// Public-accessible (status page is public). Returns last 20 rows.

router.get('/upgrade-verifications/recent', async (req, res) => {
  try {
    const rows = await verificationsDb.getRecentUpgradeVerifications({ limit: 20 });
    res.json({ rows });
  } catch (err) {
    console.error('[upgrade-verif] recent fetch error:', err.message);
    res.status(500).json({ error: 'Failed to load verifications' });
  }
});

// ── GET /api/upgrade-verifications/connection/:id ─────────────────────────────
// Returns the latest verification for a specific connection. Used by badge tooltip.

router.get('/upgrade-verifications/connection/:id', requireAuth, async (req, res) => {
  const connectionId = parseInt(req.params.id, 10);
  if (isNaN(connectionId)) return res.status(400).json({ error: 'Invalid connection id' });

  try {
    const row = await verificationsDb.getLatestVerificationForConnection(connectionId);
    res.json({ verification: row || null });
  } catch (err) {
    console.error('[upgrade-verif] connection fetch error:', err.message);
    res.status(500).json({ error: 'Failed to fetch verification' });
  }
});

// ── POST /api/connections/:id/re-verify ───────────────────────────────────────
// Operator button: trigger a fresh verification work item via agent long-poll channel.
// Auth: session (requireAuth). Ownership enforced.

router.post('/:id/re-verify', requireAuth, async (req, res) => {
  const connectionId = parseInt(req.params.id, 10);
  if (isNaN(connectionId)) return res.status(400).json({ error: 'Invalid connection id' });

  try {
    const conn = await agentDb.getConnectionById(connectionId);
    if (!conn) return res.status(404).json({ error: 'Connection not found' });
    if (conn.user_id && conn.user_id !== req.user.id) {
      return res.status(403).json({ error: 'Access denied' });
    }

    // Enqueue verification probe as a work item for the agent
    const sent = channel.sendToAgent(connectionId, {
      type: 'run_verification_probe',
      verification_id: crypto.randomUUID(),
    });

    if (!sent) {
      return res.status(503).json({ error: 'Agent not connected. Verification probe cannot be dispatched.' });
    }

    // Clear degraded state optimistically — agent will report back
    await verificationsDb.clearUpgradeDegradedStatus(connectionId);

    res.json({ ok: true, message: 'Verification probe dispatched to agent' });
  } catch (err) {
    console.error('[upgrade-verif] re-verify error:', err.message);
    res.status(500).json({ error: 'Failed to dispatch re-verify' });
  }
});

// ── Operator alert email ───────────────────────────────────────────────────────

async function sendDegradedAlert({
  connectionId, connectionName, fromVersion, toVersion,
  payload, checksOk, apiTestOk, probe7Ok, probe8Ok,
}) {
  if (!POLSIA_API_KEY || !ALERT_EMAIL) {
    console.warn('[upgrade-verif] alert skipped: POLSIA_API_KEY or ALERT_EMAIL not set');
    return;
  }

  const failReasons = [
    !checksOk    && `Health checks: ${payload.checks_passed}/${payload.checks_total} (need ≥54)`,
    !apiTestOk   && `POST /api/test returned ${payload.api_test_status || 'no response'} (expected 410)`,
    !probe7Ok    && `Probe 7 (stale version check): FAIL`,
    !probe8Ok    && `Probe 8 (config source unique): FAIL`,
  ].filter(Boolean);

  const subject = `⚠️ Post-upgrade verification FAILED — ${connectionName || `Connection #${connectionId}`}`;
  const connUrl = `${APP_URL}/connections`;

  const htmlBody = `
    <div style="font-family:'Space Grotesk',-apple-system,sans-serif;max-width:600px;margin:0 auto;background:#0a0a0c;color:#e8e8ed;padding:24px;border-radius:12px">
      <div style="display:flex;align-items:center;gap:10px;margin-bottom:24px">
        <div style="background:#f0a830;width:32px;height:32px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-weight:700;color:#0a0a0c">TV</div>
        <span style="font-size:20px;font-weight:700;letter-spacing:-.5px">TuneVault · Upgrade Verification</span>
      </div>

      <div style="background:#1c1300;border:1px solid #713f12;border-radius:10px;padding:20px;margin-bottom:20px">
        <div style="font-size:18px;font-weight:700;color:#f59e0b;margin-bottom:8px">⚠️ Post-upgrade verification FAILED</div>
        <div style="color:#8888a0;font-size:14px">
          Connection: <strong style="color:#e8e8ed">${escHtml(connectionName || String(connectionId))}</strong><br>
          Upgrade: <code style="background:#1a1a1e;padding:2px 6px;border-radius:4px">${escHtml(fromVersion || '?')} → ${escHtml(toVersion || '?')}</code>
        </div>
      </div>

      <div style="margin-bottom:20px">
        <div style="font-size:14px;font-weight:600;margin-bottom:10px;color:#e8e8ed">What failed:</div>
        <ul style="margin:0;padding-left:20px;color:#8888a0;font-size:13px;line-height:1.8">
          ${failReasons.map(r => `<li style="color:#ef4444">${escHtml(r)}</li>`).join('')}
        </ul>
      </div>

      <div style="background:#111114;border:1px solid #2a2a30;border-radius:8px;padding:16px;margin-bottom:20px;font-size:12px;color:#8888a0">
        <strong style="color:#e8e8ed">Agent is still on v${escHtml(toVersion || '?')}</strong> — no rollback performed (too risky).<br>
        Use the Re-verify button on the connections page to trigger a fresh probe, or SSH in to debug.
      </div>

      ${payload.error_detail ? `<div style="margin-bottom:16px;padding:12px;background:#1a0808;border:1px solid #7b0000;border-radius:8px;font-family:monospace;font-size:12px;color:#ff6b6b;word-break:break-word">${escHtml(payload.error_detail.slice(0,500))}</div>` : ''}

      <a href="${connUrl}" style="display:inline-block;padding:12px 24px;background:#f0a830;color:#0a0a0c;text-decoration:none;border-radius:8px;font-weight:600;font-size:14px">
        View connections →
      </a>
    </div>
  `;

  const textBody = `Post-upgrade verification FAILED.\n\nConnection: ${connectionName || connectionId}\nUpgrade: ${fromVersion} → ${toVersion}\n\nFailed:\n${failReasons.map(r => `- ${r}`).join('\n')}\n\nView: ${connUrl}`;

  await fetch(`${EMAIL_API_URL}/send`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${POLSIA_API_KEY}` },
    body: JSON.stringify({ to: ALERT_EMAIL, subject, body: textBody, html: htmlBody }),
  });

  console.log(`[upgrade-verif] degraded alert sent for connection ${connectionId}`);
}

function escHtml(s) {
  return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}

module.exports = router;
