#!/usr/bin/env node
/**
 * Rebreak GitHub Webhook Listener
 *
 * Empfängt GitHub push-Events, validiert HMAC-SHA256-Signatur,
 * und triggert das deploy.sh Script im Hintergrund.
 *
 * Port: 9000 (intern, wird von nginx reverse-proxied)
 * Secret: GITHUB_WEBHOOK_SECRET in /etc/environment (via Infisical)
 */

import http from "http";
import crypto from "crypto";
import { spawn } from "child_process";
import { readFileSync } from "fs";

const REPO_ROOT = "/srv/rebreak";
const DEPLOY_SCRIPT = `${REPO_ROOT}/scripts/deploy.sh`;
const PORT = 9000;

// Lade GITHUB_WEBHOOK_SECRET aus /etc/environment
function loadEnvFile() {
  try {
    const content = readFileSync("/etc/environment", "utf-8");
    for (const line of content.split("\n")) {
      const match = line.match(/^([^=]+)="?([^"]*)"?$/);
      if (match) process.env[match[1]] = match[2];
    }
  } catch {
    // /etc/environment nicht lesbar – Env-Vars müssen anderweitig gesetzt sein
  }
}
loadEnvFile();

const WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
  console.error(
    "[Webhook] FATAL: GITHUB_WEBHOOK_SECRET nicht in /etc/environment gesetzt",
  );
  process.exit(1);
}

function verifySignature(secret, signature, payload) {
  const hmac = crypto.createHmac("sha256", secret);
  hmac.update(payload, "utf-8");
  const digest = `sha256=${hmac.digest("hex")}`;
  try {
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(digest),
    );
  } catch {
    return false;
  }
}

// Deploy-Queue: verhindert parallele Builds (OOM-Schutz auf 4 GB RAM)
let deployRunning = false;
let pendingDeploy = false;

function runDeploy() {
  if (deployRunning) {
    pendingDeploy = true;
    console.log("[Webhook] Deploy already running – queued next deploy");
    return;
  }

  deployRunning = true;
  console.log("[Webhook] Starting deploy.sh...");

  const proc = spawn("bash", [DEPLOY_SCRIPT], {
    cwd: REPO_ROOT,
    stdio: ["ignore", "pipe", "pipe"],
    detached: false,
  });

  proc.stdout.on("data", (d) => process.stdout.write(`[deploy] ${d}`));
  proc.stderr.on("data", (d) => process.stderr.write(`[deploy:err] ${d}`));

  proc.on("close", (code) => {
    deployRunning = false;
    console.log(`[Webhook] deploy.sh exited with code ${code}`);
    if (pendingDeploy) {
      pendingDeploy = false;
      console.log("[Webhook] Running queued deploy...");
      runDeploy();
    }
  });

  proc.on("error", (err) => {
    deployRunning = false;
    console.error("[Webhook] deploy.sh spawn error:", err.message);
    if (pendingDeploy) {
      pendingDeploy = false;
      runDeploy();
    }
  });
}

const server = http.createServer((req, res) => {
  if (req.method !== "POST" || req.url !== "/webhook") {
    res.writeHead(404);
    res.end("Not found");
    return;
  }

  let body = "";
  req.on("data", (chunk) => (body += chunk));
  req.on("end", () => {
    const sig = req.headers["x-hub-signature-256"];
    if (!sig) {
      res.writeHead(401);
      res.end(JSON.stringify({ error: "Missing signature" }));
      return;
    }

    if (!verifySignature(WEBHOOK_SECRET, sig, body)) {
      res.writeHead(401);
      res.end(JSON.stringify({ error: "Invalid signature" }));
      return;
    }

    let payload;
    try {
      payload = JSON.parse(body);
    } catch {
      res.writeHead(400);
      res.end(JSON.stringify({ error: "Invalid JSON" }));
      return;
    }

    const branch = payload.ref?.split("/").pop();
    if (branch !== "main") {
      console.log(`[Webhook] Ignoring non-main branch: ${branch}`);
      res.writeHead(200);
      res.end(JSON.stringify({ ok: false, reason: "Not main branch", branch }));
      return;
    }

    const changedFiles = (payload.commits || []).flatMap((c) => [
      ...(c.added || []),
      ...(c.modified || []),
      ...(c.removed || []),
    ]);

    console.log(
      `[Webhook] Push to main by ${payload.pusher?.name} – ${changedFiles.length} files changed`,
    );
    console.log("[Webhook] Changed files:", changedFiles.slice(0, 10));

    res.writeHead(202);
    res.end(
      JSON.stringify({
        ok: true,
        message: deployRunning
          ? "Deploy queued (previous still running)"
          : "Deploy started",
        files: changedFiles.length,
      }),
    );

    // Asynchron deployen – Response ist bereits gesendet
    runDeploy();
  });
});

server.listen(PORT, "127.0.0.1", () => {
  console.log(`[Webhook] Listening on http://127.0.0.1:${PORT}/webhook`);
  console.log(`[Webhook] Secret loaded: ${WEBHOOK_SECRET ? "yes" : "NO – FATAL"}`);
});

process.on("SIGTERM", () => {
  console.log("[Webhook] SIGTERM received, shutting down");
  server.close(() => process.exit(0));
});
