"use server";

import { JWT } from "@/utils/jwt";
import {
  createToken,
  deleteTokenByUID,
  deleteTokenByUserId,
  deleteTokenExpired,
  getTokenBy,
  getUserByUsername,
  updateTokenBy,
} from "@/models/users";
import {
  comparePassword,
  spliceObject,
  hashPassword,
  getSecondOf,
} from "@/utils/library";
import { cookies } from "next/headers";
import { getUserById } from "@/models/users";
import { v6 as uuid } from "uuid";
import { redirect } from "next/navigation";
import { removeTokenCookies } from "@/utils/cookie";

export const login = async (user) => {
  try {
    const data = Object.fromEntries(user);
    const detail = JSON.parse(data.detail);
    const jwt = new JWT();
    const Cookies = await cookies();
    const uid = uuid();
    const expireIn = new Date(
      Date.now() + getSecondOf(process.env.REFRESH_TOKEN_EXPIRATION) * 1000
    );

    // delete if date more than today
    await deleteTokenExpired();

    const result = await getUserByUsername(data.username);
    // if user not found
    if (!result) throw new Error("username");

    if (!result?.status && typeof result?.status === "boolean")
      throw Error("disabled");

    const isPasswordMatch = comparePassword(data.password, result.password);
    if (!isPasswordMatch) throw new Error("password");

    const tokenData = jwt.generateCredentialCookie(
      { ...result, uid },
      {
        accessToken: ["name", "username", "role", "_id"],
        refreshToken: ["_id", "username", "uid"],
      }
    );

    const refreshTokenHashed = hashPassword(tokenData.token.refreshToken);
    const tokensData = {
      userId: result._id,
      tokenHashed: refreshTokenHashed,
      uid,
      ...detail,
      revoke: false,
      expireIn,
    };

    await createToken(tokensData);

    // set cookies
    await Promise.all(
      tokenData.cookie.map(async (cookie) => Cookies.set(cookie))
    );

    return {
      status: true,
      message: "Login Success",
      data: {
        logedIn: true,
        ...spliceObject(result, ["password"]).spliced,
        _id: result._id.toString(),
      },
    };
  } catch (error) {
    return {
      status: false,
      message: error.message,
      data: null,
    };
  }
};

export const logout = async (id) => {
  const result = await deleteTokenByUserId(id);
  await removeTokenCookies();

  return result;
};

export const requestNewToken = async (refreshToken) => {
  const jwt = new JWT();

  if (!refreshToken) throw new Error("Unauthorized");

  const data = jwt.decodeToken(refreshToken);
  const result = await getUserById(data._id);

  const tokenData = await getTokenBy({ uid: data.uid, revoke: false });
  if (!tokenData) {
    await deleteTokenByUID(data.uid);
    throw new Error("Unauthorized");
  }

  const isMatch = comparePassword(refreshToken, tokenData.tokenHashed);
  if (!isMatch) throw new Error("Unauthorized");

  const newTokenData = jwt.generateCredentialCookie(
    { ...result, uid: data.uid },
    {
      accessToken: ["name", "username", "role", "_id"],
      refreshToken: ["_id", "username", "uid"],
    }
  );

  const refreshTokenHashed = hashPassword(newTokenData.token.refreshToken);
  const tokensData = {
    tokenHashed: refreshTokenHashed,
    uid: data.uid,
  };

  await updateTokenBy({ uid: data.uid }, tokensData);

  return newTokenData;
};
