"use server";

import { randomBytes } from "node:crypto";
import { Prisma, RoomVisibility } from "@prisma/client";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { prisma } from "./prisma";
import { requireCurrentUser, userIsAdmin } from "./session";
import { getAppSettings } from "./settings";

function normalizeSlug(value: string) {
  return value
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9_-]+/g, "-")
    .replace(/^-+|-+$/g, "")
    .slice(0, 48);
}

export async function createRoom(formData: FormData) {
  const user = await requireCurrentUser();
  const settings = await getAppSettings();
  const name = String(formData.get("name") || "").trim();
  const visibility = String(formData.get("visibility") || settings.defaultRoomVisibility);
  const baseSlug = normalizeSlug(name);

  if (!name || !baseSlug) {
    redirect("/rooms?error=invalid-room");
  }

  let slug = baseSlug;
  let suffix = 2;
  while (await prisma.room.findUnique({ where: { slug }, select: { id: true } })) {
    slug = `${baseSlug}-${suffix}`;
    suffix += 1;
  }

  const allowedVisibility: RoomVisibility[] = ["PUBLIC", "FRIENDS", "EXPLICIT", "ROLE_RESTRICTED"];
  const roomVisibility = allowedVisibility.includes(visibility as RoomVisibility)
    ? (visibility as RoomVisibility)
    : "FRIENDS";

  const room = await prisma.room.create({
    data: {
      name,
      slug,
      ownerId: user.id,
      visibility: roomVisibility
    },
    select: { slug: true }
  });

  revalidatePath("/rooms");
  revalidatePath("/dashboard");
  redirect(`/rooms/${encodeURIComponent(room.slug)}`);
}

export async function deleteRoom(formData: FormData) {
  const user = await requireCurrentUser();
  const roomId = String(formData.get("roomId") || "");
  if (!roomId) return;

  const room = await prisma.room.findUnique({
    where: { id: roomId },
    include: { members: { where: { userId: user.id }, select: { canManage: true } } }
  });

  if (!room || room.isPersonal || !canManageRoom(user, room)) return;

  await prisma.$transaction([
    prisma.auditEvent.create({
      data: {
        actorId: user.id,
        roomId: room.id,
        action: "room.delete",
        metadata: { slug: room.slug, name: room.name }
      }
    }),
    prisma.room.delete({ where: { id: room.id } })
  ]);

  revalidatePath("/rooms");
  revalidatePath("/dashboard");
  redirect("/rooms?deleted=1");
}

export async function resetPersonalRoom(formData: FormData) {
  const user = await requireCurrentUser();
  const roomId = String(formData.get("roomId") || "");
  if (!roomId) return;

  const room = await prisma.room.findUnique({
    where: { id: roomId },
    include: { members: { where: { userId: user.id }, select: { canManage: true } } }
  });

  if (!room || !room.isPersonal || !canManageRoom(user, room)) return;

  await prisma.$transaction([
    prisma.mediaSource.deleteMany({ where: { roomId: room.id } }),
    prisma.roomMessage.deleteMany({ where: { roomId: room.id } }),
    prisma.room.update({ where: { id: room.id }, data: { currentState: Prisma.JsonNull } }),
    prisma.auditEvent.create({
      data: {
        actorId: user.id,
        roomId: room.id,
        action: "room.reset",
        metadata: { slug: room.slug }
      }
    })
  ]);

  revalidateRoom(room.slug);
  redirect(`/rooms/${encodeURIComponent(room.slug)}?rail=Settings&saved=1`);
}

export async function updateRoomSettings(formData: FormData) {
  const user = await requireCurrentUser();
  const roomId = String(formData.get("roomId") || "");
  const name = String(formData.get("name") || "").trim().slice(0, 80);
  const visibility = String(formData.get("visibility") || "FRIENDS");

  const room = await prisma.room.findUnique({
    where: { id: roomId },
    include: { members: { where: { userId: user.id }, select: { canManage: true } } }
  });

  if (!room || !canManageRoom(user, room) || !name) return;

  const allowedVisibility: RoomVisibility[] = ["PUBLIC", "FRIENDS", "EXPLICIT", "ROLE_RESTRICTED"];
  const roomVisibility = allowedVisibility.includes(visibility as RoomVisibility)
    ? (visibility as RoomVisibility)
    : room.visibility;

  await prisma.room.update({
    where: { id: room.id },
    data: {
      name,
      visibility: roomVisibility
    }
  });

  revalidateRoom(room.slug);
  redirect(`/rooms/${encodeURIComponent(room.slug)}?rail=Settings&saved=1`);
}

export async function addRoomMember(formData: FormData) {
  const user = await requireCurrentUser();
  const roomId = String(formData.get("roomId") || "");
  const username = String(formData.get("username") || "").trim().toLowerCase();
  const canManage = formData.get("canManage") === "on";

  const room = await prisma.room.findUnique({
    where: { id: roomId },
    include: { members: { where: { userId: user.id }, select: { canManage: true } } }
  });

  if (!room || !canManageRoom(user, room) || !username) return;

  const invited = await prisma.user.findUnique({ where: { username }, select: { id: true } });
  if (!invited || invited.id === room.ownerId) {
    redirect(`/rooms/${encodeURIComponent(room.slug)}?rail=Invite&error=user`);
  }

  await prisma.roomMember.upsert({
    where: { roomId_userId: { roomId: room.id, userId: invited.id } },
    update: { canManage },
    create: { roomId: room.id, userId: invited.id, canManage }
  });

  revalidateRoom(room.slug);
  redirect(`/rooms/${encodeURIComponent(room.slug)}?rail=Invite&saved=1`);
}

export async function createRoomInvite(formData: FormData) {
  const user = await requireCurrentUser();
  const roomId = String(formData.get("roomId") || "");
  const expiresDays = Number(formData.get("expiresDays") || 0);
  if (!roomId) return;

  const room = await prisma.room.findUnique({
    where: { id: roomId },
    include: { members: { where: { userId: user.id }, select: { canManage: true } } }
  });

  if (!room || !canManageRoom(user, room)) return;

  const expiresAt = Number.isFinite(expiresDays) && expiresDays > 0 ? new Date(Date.now() + expiresDays * 24 * 60 * 60 * 1000) : null;
  const invite = await prisma.invite.create({
    data: {
      code: randomBytes(12).toString("base64url"),
      creatorId: user.id,
      roomId: room.id,
      expiresAt
    }
  });

  await prisma.auditEvent.create({
    data: {
      actorId: user.id,
      roomId: room.id,
      action: "room.invite.create",
      metadata: { inviteId: invite.id, expiresAt: expiresAt?.toISOString() || null }
    }
  });

  revalidateRoom(room.slug);
  redirect(`/rooms/${encodeURIComponent(room.slug)}?rail=Invite&saved=1`);
}

export async function revokeRoomInvite(formData: FormData) {
  const user = await requireCurrentUser();
  const inviteId = String(formData.get("inviteId") || "");
  if (!inviteId) return;

  const invite = await prisma.invite.findUnique({
    where: { id: inviteId },
    include: {
      room: {
        include: { members: { where: { userId: user.id }, select: { canManage: true } } }
      }
    }
  });

  if (!invite?.room || !canManageRoom(user, invite.room)) return;

  await prisma.$transaction([
    prisma.invite.update({ where: { id: invite.id }, data: { status: "REVOKED" } }),
    prisma.auditEvent.create({
      data: {
        actorId: user.id,
        roomId: invite.room.id,
        action: "room.invite.revoke",
        metadata: { inviteId: invite.id }
      }
    })
  ]);

  revalidateRoom(invite.room.slug);
  redirect(`/rooms/${encodeURIComponent(invite.room.slug)}?rail=Invite&saved=1`);
}

export async function removeRoomMember(formData: FormData) {
  const user = await requireCurrentUser();
  const roomId = String(formData.get("roomId") || "");
  const userId = String(formData.get("userId") || "");
  if (!roomId || !userId) return;

  const room = await prisma.room.findUnique({
    where: { id: roomId },
    include: { members: { where: { userId: user.id }, select: { canManage: true } } }
  });

  if (!room || !canManageRoom(user, room) || userId === room.ownerId) return;

  await prisma.roomMember.deleteMany({ where: { roomId: room.id, userId } });
  await prisma.auditEvent.create({
    data: {
      actorId: user.id,
      roomId: room.id,
      action: "room.member.remove",
      metadata: { userId }
    }
  });

  revalidateRoom(room.slug);
  redirect(`/rooms/${encodeURIComponent(room.slug)}?rail=Invite&saved=1`);
}

function canManageRoom(
  user: Awaited<ReturnType<typeof requireCurrentUser>>,
  room: { ownerId: string | null; members: Array<{ canManage: boolean }> }
) {
  return room.ownerId === user.id || userIsAdmin(user) || room.members.some((member) => member.canManage);
}

function revalidateRoom(slug: string) {
  revalidatePath(`/rooms/${encodeURIComponent(slug)}`);
  revalidatePath("/rooms");
  revalidatePath("/dashboard");
}
