import { cookies } from 'next/headers';
import { randomBytes } from 'crypto';

const CSRF_TOKEN_COOKIE_NAME = 'csrf_token';
const CSRF_TOKEN_LENGTH = 32;

/**
 * Generate a cryptographically secure CSRF token
 */
export function generateCsrfToken(): string {
  return randomBytes(CSRF_TOKEN_LENGTH).toString('base64url');
}

/**
 * Get the current CSRF token from cookies or generate a new one
 */
export async function getCsrfToken(): Promise<string> {
  const cookieStore = await cookies();
  const existingToken = cookieStore.get(CSRF_TOKEN_COOKIE_NAME);

  if (existingToken?.value) {
    return existingToken.value;
  }

  const newToken = generateCsrfToken();
  await setCsrfToken(newToken);
  return newToken;
}

/**
 * Set the CSRF token in an httpOnly cookie
 */
export async function setCsrfToken(token: string): Promise<void> {
  const cookieStore = await cookies();

  try {
    cookieStore.set(CSRF_TOKEN_COOKIE_NAME, token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: 60 * 60 * 24, // 24 hours
      path: '/',
    });
  } catch {
    // Handle server component context where cookies can't be set
  }
}

/**
 * Validate a CSRF token against the stored token in cookies
 */
export async function validateCsrfToken(token: string): Promise<boolean> {
  const cookieStore = await cookies();
  const storedToken = cookieStore.get(CSRF_TOKEN_COOKIE_NAME);

  if (!storedToken?.value || !token) {
    return false;
  }

  // Constant-time comparison to prevent timing attacks
  return timingSafeEqual(
    Buffer.from(storedToken.value),
    Buffer.from(token)
  );
}

/**
 * Timing-safe string comparison to prevent timing attacks
 */
function timingSafeEqual(a: Buffer, b: Buffer): boolean {
  if (a.length !== b.length) {
    return false;
  }

  let result = 0;
  for (let i = 0; i < a.length; i++) {
    result |= a[i] ^ b[i];
  }

  return result === 0;
}

/**
 * Delete the CSRF token cookie
 */
export async function deleteCsrfToken(): Promise<void> {
  const cookieStore = await cookies();

  try {
    cookieStore.delete(CSRF_TOKEN_COOKIE_NAME);
  } catch {
    // Handle server component context
  }
}
