/**
 * POST /api/webhooks/coolify?projectId={projectId}
 *
 * Receives deployment status events from Coolify and updates the project's
 * contextSnapshot.lastDeployment in Postgres.
 *
 * Closes S-06: signature verification was missing. Coolify (≥4.0.0-beta.300)
 * signs every webhook with HMAC-SHA256 of the raw body using the per-app
 * `webhook_secret`. We now reject unsigned / mismatched requests.
 *
 * Configure: in Coolify, set the webhook target to
 *   https://vibnai.com/api/webhooks/coolify?projectId={projectId}
 * AND set the per-app webhook secret to match `COOLIFY_WEBHOOK_SECRET`.
 */

import { NextRequest, NextResponse } from "next/server";
import { query } from "@/lib/db-postgres";
import { verifyCoolifySignature } from "@/lib/server/coolify-webhook";
import { log } from "@/lib/server/logger";

const COOLIFY_WEBHOOK_SECRET = process.env.COOLIFY_WEBHOOK_SECRET ?? "";

export async function POST(request: NextRequest) {
  const projectId = request.nextUrl.searchParams.get("projectId");
  if (!projectId) {
    return NextResponse.json({ error: "Missing projectId" }, { status: 400 });
  }

  if (!COOLIFY_WEBHOOK_SECRET) {
    log.error("coolify webhook: COOLIFY_WEBHOOK_SECRET not configured", {
      route: "api.webhooks.coolify",
      projectId,
    });
    return NextResponse.json(
      { error: "Webhook receiver not configured" },
      { status: 503 },
    );
  }

  // We need the RAW body for HMAC verification — `.json()` would
  // re-serialize and could change byte order. Read text first, then parse.
  const rawBody = await request.text();
  const signature = request.headers.get("x-coolify-signature-256");

  const ok = await verifyCoolifySignature(
    rawBody,
    signature,
    COOLIFY_WEBHOOK_SECRET,
  );
  if (!ok) {
    log.warn("coolify webhook: invalid signature", {
      route: "api.webhooks.coolify",
      projectId,
      hasHeader: !!signature,
    });
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  let payload: Record<string, unknown>;
  try {
    payload = JSON.parse(rawBody) as Record<string, unknown>;
  } catch {
    return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
  }

  const rows = await query<{ id: string; data: Record<string, unknown> }>(
    `SELECT id, data FROM fs_projects WHERE id = $1 LIMIT 1`,
    [projectId],
  );

  if (rows.length === 0) {
    return NextResponse.json({ error: "Project not found" }, { status: 404 });
  }

  const project = rows[0];
  const existingSnapshot =
    (project.data?.contextSnapshot as Record<string, unknown>) ?? {};

  // Coolify sends status events like: queued, in_progress, finished, failed, cancelled
  const data = (payload.data ?? {}) as Record<string, unknown>;
  const status = (payload.status ?? data.status ?? "unknown") as string;
  const applicationUuid = (payload.application_uuid ??
    data.application_uuid) as string | undefined;
  const deploymentUuid = (payload.deployment_uuid ?? data.deployment_uuid) as
    | string
    | undefined;
  const url = (payload.fqdn ?? data.fqdn ?? null) as string | null;

  const newSnapshot = {
    ...existingSnapshot,
    lastDeployment: {
      status,
      applicationUuid,
      deploymentUuid,
      url,
      timestamp: new Date().toISOString(),
    },
    updatedAt: new Date().toISOString(),
  };

  await query(
    `UPDATE fs_projects
     SET data = jsonb_set(data, '{contextSnapshot}', $1::jsonb)
     WHERE id = $2`,
    [JSON.stringify(newSnapshot), projectId],
  );

  log.info("coolify webhook received", {
    route: "api.webhooks.coolify",
    projectId,
    status,
    deploymentUuid,
  });
  return NextResponse.json({ ok: true, status, projectId });
}
