"use server";

import { compare, hash } from "bcryptjs";
import { redirect } from "next/navigation";
import { Prisma, type User } from "@prisma/client";
import { prisma } from "./prisma";
import { clearSession, setSession } from "./session";
import { getAppSettings } from "./settings";

function normalizeUsername(value: FormDataEntryValue | null) {
  return String(value || "")
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9_-]/g, "");
}

export async function registerUser(formData: FormData) {
  const settings = await getAppSettings();
  if (settings.registrationMode === "DISABLED") {
    redirect("/register?error=closed");
  }

  const username = normalizeUsername(formData.get("username"));
  const password = String(formData.get("password") || "");
  const inviteCode = String(formData.get("inviteCode") || "").trim();

  if (!username || password.length < 10) {
    redirect("/register?error=invalid");
  }

  const invite =
    settings.registrationMode === "INVITE_ONLY"
      ? await prisma.invite.findUnique({ where: { code: inviteCode } })
      : null;

  if (
    settings.registrationMode === "INVITE_ONLY" &&
    (!invite || invite.status !== "ACTIVE" || (invite.expiresAt && invite.expiresAt.getTime() < Date.now()))
  ) {
    redirect("/register?error=invite");
  }

  const passwordHash = await hash(password, 12);
  let user: User;

  try {
    user = await prisma.$transaction(async (tx) => {
      const created = await tx.user.create({
        data: {
          username,
          displayName: username,
          passwordHash,
          ownedRooms: {
            create: {
              slug: `@${username}`,
              name: `${username}'s room`,
              isPersonal: true,
              visibility: "FRIENDS"
            }
          }
        }
      });
      if (invite) {
        await tx.invite.update({
          where: { id: invite.id },
          data: { status: "USED", usedById: created.id, usedAt: new Date() }
        });
        if (invite.roomId) {
          await tx.roomMember.upsert({
            where: { roomId_userId: { roomId: invite.roomId, userId: created.id } },
            update: {},
            create: { roomId: invite.roomId, userId: created.id }
          });
        }
      }
      return created;
    });
  } catch (error) {
    if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
      redirect("/register?error=username");
    }
    throw error;
  }

  await setSession(user.id);
  redirect("/dashboard");
}

export async function loginUser(formData: FormData) {
  const username = normalizeUsername(formData.get("username"));
  const password = String(formData.get("password") || "");

  const user = await prisma.user.findUnique({ where: { username } });
  if (!user) {
    redirect("/login?error=credentials");
  }
  if (user.disabledAt) {
    redirect("/login?error=disabled");
  }

  const ok = await compare(password, user.passwordHash);
  if (!ok) {
    redirect("/login?error=credentials");
  }

  await setSession(user.id);
  redirect("/dashboard");
}

export async function logoutUser() {
  await clearSession();
  redirect("/login");
}
