"use server";

import { revalidatePath } from "next/cache";
import { getSession } from "@/lib/auth";

/**
 * Offline mutation dispatcher.
 *
 * The v2 mobile admin (PR 3) introduces an IndexedDB-backed mutation
 * queue (src/lib/offline/queue.ts) and a sync engine
 * (src/lib/offline/sync.ts) that replays queued actions when the
 * device comes back online. The client side enqueues an action with a
 * name + JSON payload + idempotency key (clientActionId), then calls
 * `syncPending` which invokes this dispatcher for each pending action.
 *
 * The dispatcher is the single server-side bridge between the offline
 * queue and our SECURITY DEFINER server actions. To add a new offline-
 * capable action, append an entry to `listClientActions()` with:
 *   - a stable name (kebab- or camelCase, the client enqueues by name)
 *   - a handler that accepts the JSON payload and returns
 *     { success: true } | { success: false; error: string }
 *
 * The action names referenced by the plan (markOrderReady,
 * markOrderPickedUp, updateStopStatus, adjustProductStock) do not yet
 * exist as server actions in the v1 codebase. They're added in
 * follow-up PRs. For now the dispatcher returns `{ ok: false, error:
 * "Unknown action" }` for them, which surfaces in the OfflineBanner
 * conflict count. This keeps the structure in place without coupling
 * PR 3 to a server-side action surface it doesn't own.
 *
 * Idempotency: each enqueue carries a clientActionId (uuid v4). Real
 * idempotency requires a dedup table on the server — that's also a
 * follow-up. Until then, replays of the same action will re-execute,
 * but last-write-wins on the underlying tables is safe for the
 * mutations we ship (status flips, stock adjust).
 */

interface ClientAction {
  name: string;
  // The handler signature is intentionally loose — payload shapes vary
  // per action. The dispatcher in `sync.ts` invokes this with the raw
  // payload from IndexedDB.
  handler: (payload: unknown, clientActionId: string) => Promise<{
    success: boolean;
    error?: string;
  }>;
}

async function listClientActions(): Promise<ClientAction[]> {

await getSession();  // Wire real server actions here as they land:
  //   { name: "markOrderReady",      handler: markOrderReady },
  //   { name: "markOrderPickedUp",   handler: markOrderPickedUp },
  //   { name: "updateStopStatus",    handler: updateStopStatus },
  //   { name: "adjustProductStock",  handler: adjustProductStock },
  return [];
}

export async function dispatchClientAction(
  actionName: string,
  payload: unknown,
  clientActionId: string,
): Promise<
  | { ok: true }
  | { ok: false; conflict: string }
  | { ok: false; error: string }
> {

await getSession();  const actions = await listClientActions();
  const action = actions.find((a) => a.name === actionName);
  if (!action) {
    return { ok: false, error: "Unknown action" };
  }
  try {
    const result = await action.handler(payload, clientActionId);
    if (result.success === false) {
      const message = result.error ?? "Action failed";
      if (/conflict|stale/i.test(message)) {
        return { ok: false, conflict: message };
      }
      return { ok: false, error: message };
    }
    revalidatePath("/admin/v2/orders");
    revalidatePath("/admin/v2/orders/[id]", "page");
    revalidatePath("/admin/v2/stops");
    revalidatePath("/admin/v2/products");
    return { ok: true };
  } catch (err) {
    return { ok: false, error: (err as Error).message };
  }
}
