/**
 * CSRF protection utilities.
 *
 * Two mechanisms:
 * 1. Custom header check (X-EmDash-Request: 1) — used for authenticated API routes.
 *    Browsers block cross-origin custom headers, so presence proves same-origin.
 * 2. Origin check — used for public API routes that skip auth. Compares the Origin
 *    header against the request origin. Same approach as Astro's `checkOrigin`.
 */

import { apiError } from "./error.js";

/**
 * Origin-based CSRF check for public API routes that skip auth.
 *
 * State-changing requests (POST/PUT/DELETE) to public endpoints must either:
 *   1. Include the X-EmDash-Request: 1 header (custom header blocked cross-origin), OR
 *   2. Have an Origin header matching the request origin
 *
 * This prevents cross-origin form submissions (which can't set custom headers)
 * and cross-origin fetch (blocked by CORS unless allowed). Same-origin requests
 * always include a matching Origin header.
 *
 * Returns a 403 Response if the check fails, or null if allowed.
 */
export function checkPublicCsrf(request: Request, url: URL): Response | null {
	// Custom header present — browser blocks cross-origin custom headers
	const csrfHeader = request.headers.get("X-EmDash-Request");
	if (csrfHeader === "1") return null;

	// Check Origin header — present on all POST/PUT/DELETE from browsers
	const origin = request.headers.get("Origin");
	if (origin) {
		try {
			const originUrl = new URL(origin);
			if (originUrl.origin === url.origin) return null;
		} catch {
			// Malformed Origin — fall through to reject
		}

		return apiError("CSRF_REJECTED", "Cross-origin request blocked", 403);
	}

	// No Origin header — non-browser client (curl, server-to-server).
	// Allow these through since CSRF is a browser-specific attack vector.
	// Server-to-server requests don't carry ambient credentials (cookies).
	return null;
}
