import type { FastifyInstance } from 'fastify'
import bcrypt from 'bcrypt'
import { z } from 'zod'
import { auth } from '../auth/lucia.ts'
import prisma from '../prisma/client.ts'
import { createSessionLivekitToken, getSessionLivekitToken } from '../utils/livekit.ts'

export default function (fastify: FastifyInstance) {
  fastify.post('/register', async (req, reply) => {
    try {
      const schema = z.object({
        username: z.string().min(1),
        password: z.string().min(6),
      })
      const input = schema.parse(req.body)

      const hashed = await bcrypt.hash(input.password, 10)
      const user = await prisma.user.create({
        data: {
          username: input.username,
          password: hashed,
          displayName: input.username,
        },
      })

      const session = await auth.createSession(user.id, {
        livekitToken: await createSessionLivekitToken(user),
      })
      const cookie = auth.createSessionCookie(session.id)

      reply.setCookie(cookie.name, cookie.value, cookie.attributes)

      return {
        id: user.id,
        username: user.username,
        displayName: user.displayName,
      }
    }
    catch (err) {
      fastify.log.error(err)
      reply.code(400)

      if (err instanceof z.ZodError) {
        reply.send({ error: z.prettifyError(err) })
      }
      else {
        reply.send({ error: err.message })
      }
    }
  })

  fastify.post('/login', async (req, reply) => {
    try {
      const schema = z.object({
        username: z.string().min(1),
        password: z.string(),
      })
      const input = schema.parse(req.body)

      const user = await prisma.user.findFirst({
        where: { username: input.username },
      })

      if (!user) {
        return reply.code(404).send({ error: 'Incorrect username or password' })
      }

      const validPassword = await bcrypt.compare(input.password, user.password)
      if (!validPassword) {
        return reply.code(404).send({ error: 'Incorrect username or password' })
      }

      const session = await auth.createSession(user.id, {
        livekitToken: await createSessionLivekitToken(user),
      })
      const cookie = auth.createSessionCookie(session.id)

      reply.setCookie(cookie.name, cookie.value, cookie.attributes)

      return {
        id: user.id,
        username: user.username,
        displayName: user.displayName,
      }
    }
    catch (err) {
      fastify.log.error(err)
      reply.code(400)

      if (err instanceof z.ZodError) {
        reply.send({ error: z.prettifyError(err) })
      }
      else {
        reply.send({ error: err.message })
      }
    }
  })

  fastify.get('/me', async (req, reply) => {
    if (!req.user || !req.session) {
      reply.code(401).send(false)

      return
    }

    try {
      return {
        ...req.user,
        livekitToken: await getSessionLivekitToken(req.user, req.session),
      }
    }
    catch (err) {
      fastify.log.error(err)
      reply.code(500).send({ error: err.message })
    }
  })

  fastify.post('/logout', async (req, reply) => {
    try {
      if (req.session)
        await auth.invalidateSession(req.session.id)

      const blank = auth.createBlankSessionCookie()

      reply.setCookie(blank.name, blank.value, blank.attributes)

      return true
    }
    catch (err) {
      fastify.log.error(err)
      reply.code(400).send({ error: err.message })
    }
  })
}
