import { prisma } from "@linkwarden/prisma";
import type { NextApiRequest, NextApiResponse } from "next";
import bcrypt from "bcrypt";
import { PostUserSchema } from "@linkwarden/lib/schemaValidation";
import isAuthenticatedRequest from "../../isAuthenticatedRequest";
import { Subscription, User } from "@linkwarden/prisma/client";

const emailEnabled =
  process.env.EMAIL_FROM && process.env.EMAIL_SERVER ? true : false;
const stripeEnabled = process.env.STRIPE_SECRET_KEY ? true : false;

interface Data {
  response: string | object;
  status: number;
}

export default async function postUser(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<Data> {
  const parentUser = await isAuthenticatedRequest({ req });
  const isAdmin =
    parentUser && parentUser.id === Number(process.env.NEXT_PUBLIC_ADMIN || 1);

  if (process.env.NEXT_PUBLIC_DISABLE_REGISTRATION === "true" && !isAdmin) {
    return { response: "Registration is disabled.", status: 400 };
  }

  const dataValidation = PostUserSchema().safeParse(req.body);

  if (!dataValidation.success) {
    return {
      response: `Error: ${
        dataValidation.error.issues[0].message
      } [${dataValidation.error.issues[0].path.join(", ")}]`,
      status: 400,
    };
  }

  const { name, email, password, invite, acceptPromotionalEmails } =
    dataValidation.data;
  let { username } = dataValidation.data;

  if (invite && (!stripeEnabled || !emailEnabled)) {
    return { response: "You are not authorized to invite users.", status: 401 };
  } else if (invite && !parentUser) {
    return { response: "You must be logged in to invite users.", status: 401 };
  }

  const autoGeneratedUsername = "user" + Math.round(Math.random() * 1000000000);

  if (!username) {
    username = autoGeneratedUsername;
  }

  if (!emailEnabled && !password) {
    return {
      response: "Password is required.",
      status: 400,
    };
  }

  const checkIfUserExists = await prisma.user.findFirst({
    where: {
      OR: [
        {
          email: email ? email.toLowerCase().trim() : undefined,
        },
        {
          username: username ? username.toLowerCase().trim() : undefined,
        },
      ],
    },
  });

  if (!checkIfUserExists) {
    const autoGeneratedUsername =
      "user" + Math.round(Math.random() * 1000000000);

    const saltRounds = 10;

    const hashedPassword = bcrypt.hashSync(password || "", saltRounds);

    const user = await prisma.user.create({
      data: {
        name: name,
        username: emailEnabled ? username || autoGeneratedUsername : username,
        email: emailEnabled ? email : undefined,
        emailVerified: isAdmin ? new Date() : undefined,
        password: password ? hashedPassword : undefined,
        parentSubscription:
          parentUser && invite
            ? {
                connect: {
                  id: (parentUser.subscriptions as Subscription).id,
                },
              }
            : undefined,
        subscriptions:
          stripeEnabled && isAdmin
            ? {
                create: {
                  stripeSubscriptionId:
                    "fake_sub_" + Math.round(Math.random() * 10000000000000),
                  active: true,
                  currentPeriodStart: new Date(),
                  currentPeriodEnd: new Date(
                    new Date().setFullYear(new Date().getFullYear() + 1000)
                  ), // 1000 years from now
                },
              }
            : undefined,
        acceptPromotionalEmails: acceptPromotionalEmails || false,
        dashboardSections: {
          createMany: {
            data: [
              {
                order: 0,
                type: "STATS",
              },
              {
                order: 1,
                type: "RECENT_LINKS",
              },
              {
                order: 2,
                type: "PINNED_LINKS",
              },
            ],
          },
        },
      },
      select: isAdmin
        ? {
            id: true,
            username: true,
            email: true,
            emailVerified: true,
            password: true,
            subscriptions: {
              select: {
                active: true,
              },
            },
            createdAt: true,
          }
        : undefined,
    });

    const { password: pass, ...userWithoutPassword } = user as User;
    return { response: userWithoutPassword, status: 201 };
  } else {
    return { response: "Email or Username already exists.", status: 400 };
  }
}
