// src/lib/offline/queue.ts
import { v4 as uuidv4 } from "uuid";
import { getOfflineDB, type QueuedAction } from "./db";

export interface EnqueueOptions {
  id?: string;          // supply for idempotency
}

export async function enqueueAction(
  actionName: string,
  payload: unknown,
  options: EnqueueOptions = {}
): Promise<QueuedAction> {
  const db = await getOfflineDB();
  const id = options.id ?? uuidv4();
  // Atomic read-then-write inside a single readwrite transaction so two
  // concurrent calls with the same explicit `id` can't both pass the
  // existence check and write twice.
  const tx = db.transaction("queue", "readwrite");
  const existing = await tx.store.get(id);
  if (existing) {
    await tx.done;
    return existing;
  }
  const action: QueuedAction = {
    id,
    actionName,
    payload,
    createdAt: Date.now(),
    attempts: 0,
    lastAttemptAt: null,
    status: "pending",
  };
  await tx.store.put(action);
  await tx.done;
  return action;
}

export async function getQueuedActions(): Promise<QueuedAction[]> {
  const db = await getOfflineDB();
  const all = await db.getAllFromIndex("queue", "by-created");
  return all.filter((a) => a.status !== "synced");
}

export async function dequeueAction(id: string): Promise<QueuedAction | null> {
  const db = await getOfflineDB();
  const tx = db.transaction("queue", "readwrite");
  const action = await tx.store.get(id);
  if (!action) {
    await tx.done;
    return null;
  }
  await tx.store.delete(id);
  await tx.done;
  return action;
}

export async function markInFlight(id: string): Promise<void> {
  const db = await getOfflineDB();
  const tx = db.transaction("queue", "readwrite");
  const action = await tx.store.get(id);
  if (!action) {
    await tx.done;
    return;
  }
  action.status = "in-flight";
  action.lastAttemptAt = Date.now();
  action.attempts += 1;
  await tx.store.put(action);
  await tx.done;
}

export async function markSynced(id: string): Promise<void> {
  const db = await getOfflineDB();
  await db.delete("queue", id);
}

export async function markConflict(id: string, error: string): Promise<void> {
  const db = await getOfflineDB();
  const tx = db.transaction("queue", "readwrite");
  const action = await tx.store.get(id);
  if (!action) {
    await tx.done;
    return;
  }
  action.status = "conflict";
  action.error = error;
  await tx.store.put(action);
  await tx.done;
}

export async function markFailed(id: string, error: string): Promise<void> {
  const db = await getOfflineDB();
  const tx = db.transaction("queue", "readwrite");
  const action = await tx.store.get(id);
  if (!action) {
    await tx.done;
    return;
  }
  action.status = "failed";
  action.error = error;
  action.attempts += 1;
  action.lastAttemptAt = Date.now();
  await tx.store.put(action);
  await tx.done;
}
