/**
 * Manus Dashboard - Webhook Handler (CI/CD Gitea)
 * Route : POST /api/webhook/gitea
 * Écoute les événements push de Gitea et déclenche le déploiement automatique.
 */
const express = require('express');
const crypto = require('crypto');
const { exec } = require('child_process');
const path = require('path');
const fs = require('fs');

const router = express.Router();

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || '';
const APPS_BASE_PATH = process.env.APPS_BASE_PATH || '/opt/manus-deploy/apps';
const DEPLOY_SCRIPT = '/opt/manus-deploy/scripts/deploy-app.sh';
const LOG_DIR = '/var/log/manus-deploy';

// Mapping dépôt Gitea -> nom du dossier application sur le serveur
const REPO_TO_APP_MAP = {
  'itinova-contacts': 'itinova-contacts',
  'itinova-podcasts': 'itinova-podcasts',
  'veille-reglementaire': 'veille-reglementaire',
  'itinova-vehicle-exchange': 'itinova-vehicle-exchange',
  'manus-dashboard': 'manus-dashboard',
};

// Déploiements en cours (évite les doubles déclenchements)
const deployingApps = new Set();

function verifyGiteaSignature(req) {
  if (!WEBHOOK_SECRET) {
    console.warn('[Webhook] AVERTISSEMENT: WEBHOOK_SECRET non défini. Validation désactivée.');
    return true;
  }
  const signature = req.headers['x-gitea-signature'] || req.headers['x-hub-signature-256'] || '';
  console.log('[Webhook DEBUG] All headers:', JSON.stringify(Object.keys(req.headers)));
  console.log('[Webhook DEBUG] x-gitea-signature:', req.headers['x-gitea-signature'] || 'ABSENT');
  console.log('[Webhook DEBUG] rawBody available:', !!req.rawBody, 'length:', req.rawBody ? req.rawBody.length : 0);
  // Utiliser rawBody si disponible (préservé avant express.json), sinon re-sérialiser
  const bodyStr = req.rawBody ? req.rawBody.toString() : JSON.stringify(req.body);
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET).update(bodyStr).digest('hex');
  const expected = `sha256=${hmac}`;
  const received = signature.startsWith('sha256=') ? signature : `sha256=${signature}`;
  try {
    return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
  } catch {
    return false;
  }
}

function runDeploy(appName, branch, commitHash, committer, broadcast) {
  if (deployingApps.has(appName)) {
    console.log(`[Webhook] Déploiement déjà en cours pour ${appName}. Ignoré.`);
    return;
  }
  deployingApps.add(appName);
  console.log(`[Webhook] Déclenchement déploiement: ${appName} (branch: ${branch}, commit: ${commitHash})`);

  if (broadcast) {
    broadcast({ type: 'deploy_started', data: { app: appName, commit: commitHash, branch, committer } });
  }

  const env = { ...process.env, APPS_BASE_PATH };
  const cmd = `bash ${DEPLOY_SCRIPT} ${appName} recette`;
  let output = '';

  const child = exec(cmd, { env, timeout: 600000 });
  child.stdout.on('data', (d) => { output += d; process.stdout.write(`[${appName}] ${d}`); });
  child.stderr.on('data', (d) => { output += d; process.stderr.write(`[${appName}] ERR: ${d}`); });

  child.on('close', (code) => {
    deployingApps.delete(appName);
    const status = code === 0 ? 'success' : 'failed';
    console.log(`[Webhook] Déploiement ${status} pour ${appName} (exit: ${code})`);
    if (broadcast) {
      broadcast({ type: 'deploy_finished', data: { app: appName, status, exitCode: code } });
    }
    try {
      fs.mkdirSync(LOG_DIR, { recursive: true });
      fs.writeFileSync(path.join(LOG_DIR, `${appName}-last-deploy.json`), JSON.stringify({
        app: appName, branch, commit: commitHash, committer, status, exitCode: code,
        timestamp: new Date().toISOString(), output: output.slice(-3000),
      }, null, 2));
    } catch (e) { console.error('[Webhook] Erreur sauvegarde statut:', e.message); }
  });
}

// POST /api/webhook/gitea
router.post('/gitea', (req, res) => {
  if (!verifyGiteaSignature(req)) {
    console.warn('[Webhook] Signature invalide. Requête rejetée.');
    return res.status(401).json({ error: 'Signature invalide' });
  }
  const event = req.headers['x-gitea-event'] || 'unknown';
  if (event !== 'push') {
    return res.status(200).json({ message: `Événement ${event} ignoré` });
  }
  const payload = req.body;
  const repoName = payload?.repository?.name || '';
  const branch = (payload?.ref || '').replace('refs/heads/', '');
  const commitHash = (payload?.after || '').slice(0, 8) || 'unknown';
  const committer = payload?.pusher?.login || payload?.pusher?.name || 'unknown';

  console.log(`[Webhook] Push: repo=${repoName}, branch=${branch}, commit=${commitHash}, by=${committer}`);

  const appName = REPO_TO_APP_MAP[repoName];
  if (!appName) return res.status(200).json({ message: `Dépôt ${repoName} non configuré` });
  if (branch !== 'main') return res.status(200).json({ message: `Branch ${branch} ignorée` });

  res.status(202).json({ message: `Déploiement de ${appName} déclenché`, commit: commitHash, branch, committer });

  // Récupérer la fonction broadcast si disponible globalement
  const broadcastFn = global.wsBroadcast || null;
  setImmediate(() => runDeploy(appName, branch, commitHash, committer, broadcastFn));
});

// GET /api/webhook/status/:appName
router.get('/status/:appName', (req, res) => {
  const { appName } = req.params;
  const statusFile = path.join(LOG_DIR, `${appName}-last-deploy.json`);
  if (!fs.existsSync(statusFile)) return res.status(404).json({ error: 'Aucun déploiement enregistré' });
  try {
    const status = JSON.parse(fs.readFileSync(statusFile, 'utf8'));
    res.json({ ...status, isDeploying: deployingApps.has(appName) });
  } catch { res.status(500).json({ error: 'Erreur lecture statut' }); }
});

// GET /api/webhook/status
router.get('/status', (req, res) => {
  try {
    fs.mkdirSync(LOG_DIR, { recursive: true });
    const files = fs.readdirSync(LOG_DIR).filter(f => f.endsWith('-last-deploy.json'));
    const statuses = files.map(f => {
      try { const d = JSON.parse(fs.readFileSync(path.join(LOG_DIR, f), 'utf8')); return { ...d, isDeploying: deployingApps.has(d.app) }; }
      catch { return null; }
    }).filter(Boolean);
    res.json({ deployments: statuses, currentlyDeploying: [...deployingApps] });
  } catch { res.status(500).json({ error: 'Erreur lecture statuts' }); }
});

module.exports = router;
