import { prisma } from "./prisma";
import { Prisma } from "@prisma/client";
import { Restaurant, SceneType } from "@/types";

const ROOM_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours

export interface RoomData {
  users: string[];
  restaurants: Restaurant[];
  likes: Record<string, string[]>;
  swipeCounts: Record<string, number>;
  match: string | null;
  creatorId: string;
  locked: boolean;
  kickedUsers: string[];
  scene: SceneType;
}

const ROOM_ID_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";

function generateRoomId(): string {
  let id = "";
  for (let i = 0; i < 6; i++) {
    id += ROOM_ID_CHARS[Math.floor(Math.random() * ROOM_ID_CHARS.length)];
  }
  return id;
}

function normalize(raw: Partial<RoomData>): RoomData {
  return {
    users: raw.users ?? [],
    restaurants: raw.restaurants ?? [],
    likes: raw.likes ?? {},
    swipeCounts: raw.swipeCounts ?? {},
    match: raw.match ?? null,
    creatorId: raw.creatorId ?? "",
    locked: raw.locked ?? false,
    kickedUsers: raw.kickedUsers ?? [],
    scene: raw.scene ?? "eat",
  };
}

let lastCleanup = 0;
const CLEANUP_INTERVAL_MS = 60 * 60 * 1000; // at most once per hour

async function cleanupExpiredRooms() {
  const now = Date.now();
  if (now - lastCleanup < CLEANUP_INTERVAL_MS) return;
  lastCleanup = now;

  try {
    const { count } = await prisma.room.deleteMany({
      where: { expiresAt: { lt: new Date() } },
    });
    if (count > 0) {
      console.log(`Cleaned up ${count} expired room(s)`);
    }
  } catch (e) {
    console.error("Room cleanup failed:", e);
  }
}

export async function createRoom(restaurants: Restaurant[], creatorId: string, scene: SceneType = "eat"): Promise<string> {
  await cleanupExpiredRooms();

  const data: RoomData = {
    users: [],
    restaurants,
    likes: {},
    swipeCounts: {},
    match: null,
    creatorId,
    locked: false,
    kickedUsers: [],
    scene,
  };

  const expiresAt = new Date(Date.now() + ROOM_TTL_MS);
  const payload = JSON.stringify(data);

  for (let attempts = 0; attempts < 20; attempts++) {
    const roomId = generateRoomId();
    try {
      await prisma.room.create({
        data: { id: roomId, data: payload, expiresAt },
      });
      return roomId;
    } catch (e) {
      if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2002") {
        continue;
      }
      throw e;
    }
  }

  throw new Error("无法生成唯一房间号，请稍后重试");
}

export async function getRoomData(
  roomId: string,
): Promise<RoomData | null> {
  const room = await prisma.room.findUnique({ where: { id: roomId } });
  if (!room) return null;
  if (room.expiresAt < new Date()) {
    prisma.room.delete({ where: { id: roomId } }).catch(() => {});
    return null;
  }
  return normalize(JSON.parse(room.data));
}

/**
 * Atomic read-modify-write within a Prisma transaction.
 * Prevents race conditions when multiple users swipe concurrently.
 */
export async function atomicUpdateRoom(
  roomId: string,
  updater: (data: RoomData) => RoomData,
): Promise<RoomData | null> {
  return prisma.$transaction(async (tx) => {
    const room = await tx.room.findUnique({ where: { id: roomId } });
    if (!room) return null;

    const data = normalize(JSON.parse(room.data));
    const updated = updater(data);

    await tx.room.update({
      where: { id: roomId },
      data: { data: JSON.stringify(updated) },
    });

    return updated;
  });
}
