import { vi } from "vitest";

/**
 * Shared mocks for the Next.js runtime pieces that server actions depend on.
 * Action tests wire these in with `vi.mock(...)` at the top of each file, e.g.:
 *
 *   vi.mock("next/navigation", async () => ({
 *     redirect: (await import("@/tests/helpers/next-mocks")).redirect,
 *   }));
 */

export class RedirectError extends Error {
  readonly digest = "NEXT_REDIRECT";
  readonly __isRedirect = true;
  constructor(public url: string) {
    super(`NEXT_REDIRECT:${url}`);
  }
}

export function redirect(url: string): never {
  throw new RedirectError(url);
}

export function isRedirectError(error: unknown): error is RedirectError {
  return (
    error instanceof RedirectError ||
    (typeof error === "object" && error !== null && (error as { __isRedirect?: boolean }).__isRedirect === true)
  );
}

export const revalidatePath = vi.fn();

export const adminAuth = { authenticated: true };
export const clearAdminSessionCookie = vi.fn(async () => {});
export async function isAdminAuthenticated(): Promise<boolean> {
  return adminAuth.authenticated;
}

/** Run an action and return the URL it redirected to (or throw if it didn't). */
export async function captureRedirect(run: () => Promise<unknown>): Promise<string> {
  try {
    await run();
  } catch (error) {
    if (isRedirectError(error)) {
      return error.url;
    }
    throw error;
  }
  throw new Error("Expected the action to redirect, but it returned normally.");
}

export function resetNextMocks() {
  revalidatePath.mockClear();
  clearAdminSessionCookie.mockClear();
  adminAuth.authenticated = true;
}

/** Build a FormData from a flat record (strings and Files). */
export function formDataFrom(fields: Record<string, string | File | undefined>): FormData {
  const formData = new FormData();
  for (const [key, value] of Object.entries(fields)) {
    if (value !== undefined) {
      formData.set(key, value);
    }
  }
  return formData;
}
