import checkPasswordIsValid from "@backend/components/checkPasswordIsValid"
import createTokenDB from "@backend/components/createTokenDB"
import getPlayerByNameDB from "@backend/components/getPlayerByNameDB"
import getUserFromBody from "@backend/components/getUserFromBody"
import sendError, { API } from "@backend/components/sendError"
import sendResponse from "@backend/components/sendResponse"
import logging from "@backend/logging"
import { rejectionErrors } from "@lib/backend/errors"
import prisma from "@lib/prisma"
import jwt from "jsonwebtoken"
import type { NextApiRequest, NextApiResponse } from "next"

interface Data {
  loggedIn: boolean
}

export default async function login(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  const context = { req, res }

  await preCheck(context)
  const { username, password } = await getUserFromBody(context)
  const player = await getPlayerByNameDB(context, username)
  await checkPasswordIsValid(context, player, password)
  const { newToken, newTokenDB } = await createTokenDB(player, "REFRESH")
  return sendResponse(context, {
    message:
      "User " +
      player.id +
      " logged in and generated Refresh-Token: " +
      newTokenDB.id,
    body: { loggedIn: true },
    type: ["debug", "infoCyan"],
    cookie: newToken.value,
  })
}

async function preCheck<T>(context: API<T>) {
  const { req } = context
  const oldRefreshToken = req.cookies.token

  try {
    if (!oldRefreshToken) throw sendError(context, rejectionErrors.noCookie)
    // Check for old cookie, if unused invalidate it
    const tokenData = jwt.verify(
      oldRefreshToken,
      process.env.TOKEN_SECRET as string
    )

    if (!tokenData || typeof tokenData === "string") return

    const oldDBToken = await prisma.token.findUniqueOrThrow({
      where: {
        id: tokenData.id,
      },
    })

    if (oldDBToken.used)
      logging("Old login token was used: " + oldDBToken.id, ["debug"], req)
    else {
      await prisma.token.update({
        where: {
          id: tokenData.id,
        },
        data: {
          used: true,
        },
      })
      await logging("Old login token has been invalidated.", ["debug"], req)
    }
  } catch (err) {
    // err is expected if no correct cookie, just continue, otherwise it has been invalidated above
  }

  return
}
