export interface ServerCallOptions {
  endpoint?: string;
  signal?: AbortSignal;
  headers?: HeadersInit;
  csrfToken?: string;
}
export class WrnServerCallError extends Error {
  constructor(
    message: string,
    readonly code: string,
    readonly status: number,
    readonly details?: unknown,
  ) {
    super(message);
  }
}

function csrfFromCookie(): string | undefined {
  if (typeof document === "undefined") return undefined;
  const raw = /(?:^|;\s*)wire-csrf=([^;]+)/.exec(document.cookie)?.[1];
  return raw ? decodeURIComponent(raw) : undefined;
}

export async function callServerFunction<TInput extends unknown[], TOutput>(
  component: string,
  functionName: string,
  args: TInput,
  options: ServerCallOptions = {},
): Promise<TOutput> {
  const csrfToken = options.csrfToken ?? csrfFromCookie();
  const response = await fetch(options.endpoint ?? "/__wrnexus/rpc", {
    method: "POST",
    credentials: "same-origin",
    signal: options.signal,
    headers: {
      "content-type": "application/json",
      ...(csrfToken ? { "x-csrf-token": csrfToken } : {}),
      ...options.headers,
    },
    body: JSON.stringify({ component, function: functionName, args }),
  });
  const payload = (await response.json().catch(() => null)) as any;
  if (!response.ok || !payload?.ok)
    throw new WrnServerCallError(
      payload?.error?.message ?? `Server call failed (${response.status})`,
      payload?.error?.code ?? "WRN-RPC-FAILED",
      response.status,
      payload?.error?.details,
    );
  return payload.value as TOutput;
}

export function createServerProxy<T extends Record<string, (...args: any[]) => Promise<any>>>(
  component: string,
  options: ServerCallOptions = {},
): T {
  return new Proxy({} as T, {
    get:
      (_target, property) =>
      (...args: unknown[]) =>
        callServerFunction(component, String(property), args, options),
  });
}
