import { and, eq, gt } from "drizzle-orm";
import { getDb } from "../../db/client.js";
import { locations, players, sessions } from "../../db/schema.js";

// ---- Pull: everything changed since a cursor (tombstones included) ----

export async function pull(ownerId: string, since: number) {
  const db = getDb();
  const serverTime = Date.now();

  const [loc, pl, se] = await Promise.all([
    db
      .select()
      .from(locations)
      .where(and(eq(locations.ownerId, ownerId), gt(locations.updatedAt, since))),
    db
      .select()
      .from(players)
      .where(and(eq(players.ownerId, ownerId), gt(players.updatedAt, since))),
    db
      .select()
      .from(sessions)
      .where(and(eq(sessions.ownerId, ownerId), gt(sessions.updatedAt, since))),
  ]);

  return { serverTime, locations: loc, players: pl, sessions: se };
}

// ---- Push: upsert client records; server clock is authoritative ----

export interface PushPayload {
  locations?: any[];
  players?: any[];
  sessions?: any[];
}

interface Applied {
  id: string;
  updatedAt: number;
}

export async function push(ownerId: string, payload: PushPayload) {
  const db = getDb();
  const now = Date.now();
  const applied = {
    locations: [] as Applied[],
    players: [] as Applied[],
    sessions: [] as Applied[],
  };

  await db.transaction(async (tx) => {
    for (const r of payload.locations ?? []) {
      if (!r?.id) continue;
      await tx
        .insert(locations)
        .values({
          id: r.id,
          ownerId,
          name: r.name ?? "",
          address: r.address ?? null,
          createdAt: r.createdAt ?? now,
          updatedAt: now,
          deletedAt: r.deletedAt ?? null,
        })
        .onConflictDoUpdate({
          target: locations.id,
          set: {
            name: r.name ?? "",
            address: r.address ?? null,
            updatedAt: now,
            deletedAt: r.deletedAt ?? null,
          },
        });
      applied.locations.push({ id: r.id, updatedAt: now });
    }

    for (const r of payload.players ?? []) {
      if (!r?.id) continue;
      await tx
        .insert(players)
        .values({
          id: r.id,
          ownerId,
          name: r.name ?? "",
          avatar: r.avatar ?? null,
          createdAt: r.createdAt ?? now,
          updatedAt: now,
          deletedAt: r.deletedAt ?? null,
        })
        .onConflictDoUpdate({
          target: players.id,
          set: {
            name: r.name ?? "",
            avatar: r.avatar ?? null,
            updatedAt: now,
            deletedAt: r.deletedAt ?? null,
          },
        });
      applied.players.push({ id: r.id, updatedAt: now });
    }

    for (const r of payload.sessions ?? []) {
      if (!r?.id) continue;
      await tx
        .insert(sessions)
        .values({
          id: r.id,
          ownerId,
          gameId: r.gameId ?? "",
          dateStart: r.dateStart ?? now,
          dateEnd: r.dateEnd ?? null,
          players: r.players ?? [],
          rounds: r.rounds ?? [],
          status: r.status ?? "finished",
          options: r.options ?? {},
          winnerIds: r.winnerIds ?? null,
          locationId: r.locationId ?? null,
          createdAt: r.dateStart ?? now,
          updatedAt: now,
          deletedAt: r.deletedAt ?? null,
        })
        .onConflictDoUpdate({
          target: sessions.id,
          set: {
            gameId: r.gameId ?? "",
            dateStart: r.dateStart ?? now,
            dateEnd: r.dateEnd ?? null,
            players: r.players ?? [],
            rounds: r.rounds ?? [],
            status: r.status ?? "finished",
            options: r.options ?? {},
            winnerIds: r.winnerIds ?? null,
            locationId: r.locationId ?? null,
            updatedAt: now,
            deletedAt: r.deletedAt ?? null,
          },
        });
      applied.sessions.push({ id: r.id, updatedAt: now });
    }
  });

  return { serverTime: now, applied };
}
