import { AuthMec } from "~/prisma/client/enums";
import prisma from "~/server/internal/db/database";
import authManager, { createHashArgon2 } from "~/server/internal/auth";
import * as jdenticon from "jdenticon";
import objectHandler from "~/server/internal/objects";
import { type } from "arktype";
import { randomUUID } from "node:crypto";
import { throwingArktype } from "~/server/arktype";
import userStatsManager from "~/server/internal/userstats";

export const SharedRegisterValidator = type({
  username: "string >= 5",
  email: "string.email",
});

const CreateUserValidator = SharedRegisterValidator.and({
  invitation: "string",
  password: "string >= 8",
  "displayName?": "string | undefined",
}).configure(throwingArktype);

export default defineEventHandler<{
  body: typeof CreateUserValidator.infer;
}>(async (h3) => {
  const t = await useTranslation(h3);

  if (!authManager.getAuthProviders().Simple)
    throw createError({
      statusCode: 403,
      message: t("errors.auth.method.signinDisabled"),
    });

  const user = await readValidatedBody(h3, CreateUserValidator);

  const invitation = await prisma.invitation.findUnique({
    where: { id: user.invitation },
  });
  if (!invitation)
    throw createError({
      statusCode: 401,
      message: t("errors.auth.invalidInvite"),
    });

  // reuse items from invite
  if (invitation.username !== null) user.username = invitation.username;
  if (invitation.email !== null) user.email = invitation.email;

  const existing = await prisma.user.count({
    where: { username: user.username },
  });
  if (existing > 0)
    throw createError({
      statusCode: 400,
      message: t("errors.auth.usernameTaken"),
    });

  const userId = randomUUID();

  const profilePictureId = randomUUID();
  await objectHandler.createFromSource(
    profilePictureId,
    async () => jdenticon.toPng(user.username, 256),
    {},
    [`internal:read`, `${userId}:read`],
  );
  const [linkMec] = await prisma.$transaction([
    prisma.linkedAuthMec.create({
      data: {
        mec: AuthMec.Simple,
        credentials: await createHashArgon2(user.password),
        version: 2,
        user: {
          create: {
            id: userId,
            username: user.username,
            displayName: user.displayName ?? user.username,
            email: user.email,
            profilePictureObjectId: profilePictureId,
            admin: invitation.isAdmin,
          },
        },
      },
      select: {
        user: true,
      },
    }),
    prisma.invitation.deleteMany({ where: { id: user.invitation } }),
  ]);

  await userStatsManager.addUser();
  return linkMec.user;
});
