import type { NextFunction, Request, Response } from "express";
import { audit } from "../lib/audit.js";
import { forbidden } from "../lib/errors.js";

export const CSRF_COOKIE = "bousla_csrf";
export const CSRF_HEADER = "x-csrf-token";

/**
 * Double-submit cookie CSRF protection: every state-changing request must echo the
 * non-httpOnly csrf cookie value in the x-csrf-token header. Rejections are audited (AUD-REQ-003).
 */
export async function csrfProtect(req: Request, _res: Response, next: NextFunction): Promise<void> {
  const method = req.method.toUpperCase();
  if (method === "GET" || method === "HEAD" || method === "OPTIONS") return next();

  const cookieToken = req.cookies?.[CSRF_COOKIE];
  const headerToken = req.headers[CSRF_HEADER];
  if (
    typeof cookieToken !== "string" ||
    cookieToken.length < 16 ||
    typeof headerToken !== "string" ||
    headerToken !== cookieToken
  ) {
    // Written before the rejection is sent: the audit trail must never lag the response (AUD-REQ-003).
    await audit({
      actorUserId: req.user?.id ?? null,
      action: "csrf.rejected",
      entityType: "endpoint",
      context: { method: req.method, path: req.path },
      ip: req.ip ?? null,
      userAgent: req.headers["user-agent"] ?? null,
    });
    return next(forbidden("رمز الحماية (CSRF) مفقود أو غير صالح"));
  }
  next();
}
