"use server"
import util from "util"
import { createHash, type Hash, randomBytes } from "crypto"
import { prisma } from "./index"
import { cf_api_keys } from "@prisma/client"

// const prisma = new PrismaClient()
async function generateRandomAPIKey(): Promise<string> {
  const randomBytesAsync = util.promisify(randomBytes)
  const token = (await randomBytesAsync(48)).toString("base64url")
  return "cf-" + token
}

export async function hashApiKey(token: string) {
  // TODO: Consider stripping the cf- prefix from the token before hashing. This would reduce the chance of
  //  a collision, and would also make it harder to brute force the token.
  const hash: Hash = createHash("sha384")
  hash.update(token)
  return hash.digest("base64url")
}

export async function genAndStoreAPITokenHash(keyName: string, userId: string): Promise<string> {
  // The idea here is to not store the API Keys in the database in plaintext. Because if we do that, then
  // the keys are vulnerable to internal and external database breaches. Instead, we store the hash
  // of the key in the database. When a user wants to use the key, they send the plaintext key to the server,
  // which hashes it and compares it to the hash in the database. If they match, then the key is valid.
  // The reason to not use bcrypt is that bcrypt is designed to be slow, which is good for passwords, but
  // not good for API keys. Also with salting, one can't simply search for a key in the DB.
  // Instead, we use SHA-384, which is designed to be fast. Rainbow table attacks are mitigated by using a long
  // random key, for which rainbow tables become unfeasible and the attacker would have to brute force the key.
  // The reason to use SHA-384 instead of SHA-256 is that SHA-384 has a longer output, which makes it much harder
  // to brute force.

  const token: string = await generateRandomAPIKey()
  const hashedToken = await hashApiKey(token)
  await prisma.cf_api_keys.create({
    data: {
      key: hashedToken,
      user_id: userId,
      suffix: token.slice(-4),
      name: keyName,
    },
  })
  return token
}

export async function userForAPIKey(key: string): Promise<null | string> {
  // TODO: Add a rate limiter to prevent brute force attacks.
  const hashedToken = await hashApiKey(key)
  try {
    const result = await prisma.cf_api_keys.update({
      where: {
        key: hashedToken,
      },
      data: {
        last_used: new Date(),
      },
    })
    if (result == null) {
      console.log(`No user found for API key ${key}`)
      return null
    } else {
      return result.user_id
    }
  } catch (e) {
    console.log(`Error in userForAPIKey: ${e}`)
    return null
  }
}

export async function deleteAPIKeyById(id: number, userId: string): Promise<cf_api_keys> {
  return prisma.cf_api_keys.delete({
    where: {
      id,
      user_id: userId,
    },
  })
}
