export interface ActionClientOptions<I> {
  signal?: AbortSignal;
  csrfToken?: string;
  headers?: HeadersInit;
  serialize?: (input: I) => BodyInit;
}

export interface ActionResult<O> {
  data: O;
  invalidated: string[];
}

export class ActionClientError extends Error {
  constructor(
    public readonly status: number,
    public readonly errors?: Record<string, string>,
  ) {
    super(`Server action failed with status ${status}.`);
    this.name = "ActionClientError";
  }
}

export function createActionClient<I, O>(route: string, name: string) {
  return async (input: I, options: ActionClientOptions<I> = {}): Promise<ActionResult<O>> => {
    const headers = new Headers(options.headers);
    headers.set("accept", "application/json");
    headers.set("x-wrnexus-action", name);
    if (options.csrfToken) headers.set("x-csrf-token", options.csrfToken);
    const body = options.serialize ? options.serialize(input) : JSON.stringify(input);
    if (!options.serialize) headers.set("content-type", "application/json");
    const response = await fetch(route, {
      method: "POST",
      credentials: "same-origin",
      signal: options.signal,
      headers,
      body,
    });
    const payload = (await response.json()) as {
      data?: O;
      invalidated?: string[];
      errors?: Record<string, string>;
    };
    if (!response.ok) throw new ActionClientError(response.status, payload.errors);
    return { data: payload.data as O, invalidated: payload.invalidated ?? [] };
  };
}
