/**
 * Shared utilities for plugin admin UIs.
 *
 * Plugin admin components (`admin.tsx`) run inside the EmDash admin dashboard.
 * This module provides the common helpers they all need: API fetching with CSRF
 * protection, response envelope unwrapping, and type narrowing.
 *
 * Import as: `import { apiFetch, parseApiResponse, isRecord } from "emdash/plugin-utils";`
 */

/**
 * Fetch wrapper that adds the `X-EmDash-Request` CSRF protection header.
 *
 * All plugin admin API calls should use this instead of raw `fetch()`.
 * State-changing endpoints reject requests without this header.
 */
export function apiFetch(input: string | URL | Request, init?: RequestInit): Promise<Response> {
	const headers = new Headers(init?.headers);
	headers.set("X-EmDash-Request", "1");
	return fetch(input, { ...init, headers });
}

/**
 * Parse an API response, unwrapping the `{ data: T }` envelope.
 *
 * All plugin API routes return success responses wrapped in `{ data: ... }`
 * by `apiSuccess()`. This helper unwraps that envelope and handles errors.
 *
 * On error responses (non-2xx), throws an Error with the server's message
 * (from `{ error: { message } }`) or the fallback message.
 *
 * @example
 * ```ts
 * const res = await apiFetch("/_emdash/api/plugins/my-plugin/items");
 * const { items } = await parseApiResponse<{ items: Item[] }>(res, "Failed to load items");
 * ```
 */
export async function parseApiResponse<T>(
	response: Response,
	fallbackMessage = "Request failed",
): Promise<T> {
	if (!response.ok) {
		throw new Error(await getErrorMessage(response, `${fallbackMessage}: ${response.statusText}`));
	}
	const body: { data: T } = await response.json();
	return body.data;
}

/**
 * Extract the error message from a failed API response.
 *
 * Error responses use the shape `{ error: { code, message } }`. This helper
 * parses that body and returns the message, falling back to the provided default.
 * Swallows JSON parse failures gracefully.
 *
 * @example
 * ```ts
 * if (!res.ok) {
 *   setError(await getErrorMessage(res, "Failed to save"));
 *   return;
 * }
 * ```
 */
export async function getErrorMessage(response: Response, fallback: string): Promise<string> {
	const body: unknown = await response.json().catch(() => ({}));
	if (isRecord(body) && isRecord(body.error)) {
		const msg = body.error.message;
		if (typeof msg === "string") return msg;
	}
	return fallback;
}

/**
 * Narrow `unknown` to a plain object record.
 *
 * Useful for safely inspecting untyped API responses before accessing properties.
 */
export function isRecord(value: unknown): value is Record<string, unknown> {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
