"use server";
import {
  registerSchemaType,
  registerSchema,
  loginSchema,
  loginSchemaType,
  forgotPasswordSchema,
  forgotPasswordSchemaType,
  newPasswordSchema,
  newPasswordSchemaType,
} from "@/schemas/auth.schema";
import { signIn } from "@/auth";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/prisma";
import { AuthError } from "next-auth";
import {
  generateVerificationToken,
  generatePasswordResetToken,
  generateTwoFactorToken,
} from "@/lib/tokens";
import {
  sendVerificationEmail,
  sendPasswordResetEmail,
  sendTwoFactorTokenEmail,
} from "@/lib/mail";
import { getVerificationTokenByToken } from "@/lib/verification-token";
import { getPasswordResetTokenByToken } from "@/lib/passwordResetToken";
import { getTwoFactorTokenByEmail } from "@/lib/two-factor-token";
import { getTwoFactorConfirmationByUserId } from "@/lib/two-factor-confirmation";

export const googleSignIn = async () => {
  await signIn("google");
};

export const register = async (values: registerSchemaType) => {
  const validatedFields = registerSchema.safeParse(values);
  if (!validatedFields.success) {
    return { error: "Invalid fields!" };
  }

  const { email, name, password } = validatedFields.data;

  const hashedPassword = await bcrypt.hash(password, 10);

  const user = await prisma.user.findUnique({
    where: {
      email,
    },
  });

  if (user) {
    return { error: "Something went wrong" };
  }

  await prisma.user.create({
    data: {
      email,
      name,
      password: hashedPassword,
    },
  });

  const verificationToken = await generateVerificationToken(email);
  await sendVerificationEmail(verificationToken.email, verificationToken.token);

  return { success: "Confirmation email sent!" };
};

export const login = async (values: loginSchemaType) => {
  const validatedFields = loginSchema.safeParse(values);

  if (!validatedFields.success) {
    return { error: "Invalid fields!" };
  }
  const { email, password, code } = validatedFields.data;

  const existingUser = await prisma.user.findUnique({
    where: {
      email,
    },
  });

  if (!existingUser || !existingUser.password || !existingUser.email) {
    return { error: "Invalid credentials!" };
  }

  const passwordMatch = await bcrypt.compare(password, existingUser.password);
  if (!passwordMatch) {
    return { error: "Invalid credentials!" };
  }

  if (!existingUser.emailVerified) {
    const verificationToken = await generateVerificationToken(
      existingUser.email,
    );
    await sendVerificationEmail(
      verificationToken.email,
      verificationToken.token,
    );

    return {
      success: "Confirmation email sent!",
      redirectUrl: "/auth/login",
    };
  }

  if (existingUser.isTwoFactorEnabled && existingUser.email) {
    if (code) {
      const twoFactorToken = await getTwoFactorTokenByEmail(existingUser.email);
      if (!twoFactorToken) {
        return { error: "Invalid two factor token!" };
      }
      if (twoFactorToken.token !== code) {
        return { error: "Invalid two factor token!" };
      }
      const hasExpired = new Date(twoFactorToken.expires) < new Date();
      if (hasExpired) {
        return { error: "Token expired!" };
      }
      await prisma.twoFactorToken.delete({
        where: {
          id: twoFactorToken.id,
        },
      });
      const existingConfirmation = await getTwoFactorConfirmationByUserId(
        existingUser.id,
      );
      if (existingConfirmation) {
        await prisma.twoFactorConfirmation.delete({
          where: {
            id: existingConfirmation.id,
          },
        });
      }
      await prisma.twoFactorConfirmation.create({
        data: {
          userId: existingUser.id,
        },
      });
    } else {
      const twoFactorToken = await generateTwoFactorToken(existingUser.email);
      await sendTwoFactorTokenEmail(twoFactorToken.email, twoFactorToken.token);
      return {
        twoFactor: true,
      };
    }
  }

  try {
    await signIn("credentials", {
      email,
      password,
      redirect: false,
    });
  } catch (error) {
    if (error instanceof AuthError) {
      switch (error.type) {
        case "CredentialsSignin":
          return { error: "Invalid credentials!" };
        default:
          return { error: "Something went wrong!" };
      }
    }
    throw error;
  }

  let redirectUrl;
  if (existingUser.role == "MODERATOR") {
    redirectUrl = "/moderator";
  } else if (existingUser.role == "ADMIN") {
    redirectUrl = "/admin";
  } else if (existingUser.role == "USER") {
    redirectUrl = "/";
  }
  return {
    success: "Logged in successfully!",
    redirectUrl: redirectUrl, // Include the redirect URL
  };
};

export const newVerification = async (token: string) => {
  const existingToken = await getVerificationTokenByToken(token);
  if (!existingToken) {
    return { error: "Invalid token!" };
  }
  const hasExpired = new Date(existingToken.expires) < new Date();
  if (hasExpired) {
    return { error: "Token expired!" };
  }
  const existingUser = await prisma.user.findUnique({
    where: {
      email: existingToken.email,
    },
  });
  if (!existingUser) {
    return { error: "Something went wrong!" };
  }
  await prisma.user.update({
    where: {
      id: existingUser.id,
    },
    data: {
      emailVerified: new Date(),
      email: existingToken.email,
    },
  });
  await prisma.verificationToken.delete({
    where: {
      id: existingToken.id,
    },
  });

  return { success: "Email verified successfully!" };
};

export const forgotPassword = async (values: forgotPasswordSchemaType) => {
  const validatedFields = forgotPasswordSchema.safeParse(values);
  if (!validatedFields.success) {
    return { error: "Invalid fields!" };
  }
  const { email } = validatedFields.data;
  const existingUser = await prisma.user.findUnique({
    where: {
      email,
    },
  });
  if (!existingUser) {
    return { error: "Something went wrong!" };
  }

  const passwordResetToken = await generatePasswordResetToken(email);
  await sendPasswordResetEmail(
    passwordResetToken.email,
    passwordResetToken.token,
  );
  return { success: "Reset email sent!" };
};

export const newPassword = async (
  values: newPasswordSchemaType,
  token?: string,
) => {
  if (!token) {
    return { error: "Missing token!" };
  }
  const validatedFields = newPasswordSchema.safeParse(values);
  if (!validatedFields.success) {
    return { error: "Invalid fields!" };
  }
  const { password } = validatedFields.data;
  const existingToken = await getPasswordResetTokenByToken(token);
  if (!existingToken) {
    return { error: "Invalid token!" };
  }
  const hasExpired = new Date(existingToken.expires) < new Date();
  if (hasExpired) {
    return { error: "Token expired!" };
  }
  const existingUser = await prisma.user.findUnique({
    where: {
      email: existingToken.email,
    },
  });
  if (!existingUser) {
    return { error: "Something went wrong!" };
  }
  const hashedPassword = await bcrypt.hash(password, 10);
  await prisma.user.update({
    where: {
      id: existingUser.id,
    },
    data: {
      password: hashedPassword,
    },
  });
  await prisma.passwordResetToken.delete({
    where: {
      id: existingToken.id,
    },
  });

  return { success: "Password updated successfully!" };
};
