import type { NextApiResponseWithSocket } from "../../interfaces/NextApiSocket"
import prisma from "@lib/prisma"
import jwt from "jsonwebtoken"
import type { NextApiRequest } from "next"
import { Server } from "socket.io"

export interface ServerToClientEvents {
  // noArg: () => void
  // basicEmit: (a: number, b: string, c: Buffer) => void
  // withAck: (d: string, callback: (e: number) => void) => void
  "update-input": (msg: string) => void
  unauthenticated: () => void
  authenticated: () => void
  test2: (test: string) => void
  test: () => void
}

export interface ClientToServerEvents {
  // hello: () => void
  "input-change": (msg: string) => void
  authenticate: (payload: { token: string }) => void
  test: (payload: any) => void
}

interface InterServerEvents {
  // ping: () => void
}

interface SocketData {
  // name: string
  // age: number
  isAuthenticated: boolean
}

// const authenticate = (socket, next) => {
//   socket.isAuthenticated = false
//
// }

async function checkTokenIsValid(rawToken: string) {
  // Verify the token and get the payload
  let tokenData: string | jwt.JwtPayload
  try {
    tokenData = jwt.verify(rawToken, process.env.TOKEN_SECRET as string)
  } catch (err: any) {
    // Deal with the problem in more detail
    return Promise.reject()
  }
  // Making sure the token data is not a string (because it should be an object)
  if (typeof tokenData === "string") return Promise.reject()

  return tokenData
}

const SocketHandler = (req: NextApiRequest, res: NextApiResponseWithSocket) => {
  if (res.socket.server.io) {
    console.log("Socket is already running " + req.url)
  } else {
    console.log("Socket is initializing " + req.url)
    const io = new Server<
      ClientToServerEvents,
      ServerToClientEvents,
      InterServerEvents,
      SocketData
    >(res.socket.server)
    res.socket.server.io = io

    // io.use(authenticate)
    io.on("connection", (socket) => {
      socket.on("input-change", (msg) => {
        if (!socket.data.isAuthenticated) socket.emit("unauthenticated")
        // socket.broadcast.emit("update-input", msg)
        io.emit("update-input", msg)
      })

      socket.on("authenticate", (payload) => {
        checkTokenIsValid(payload.token)
          .then(async ({ tokenBody }) => {
            const token = await prisma.token.findUnique({
              where: { id: tokenBody.id },
            })
            if (!token || token.used) {
              socket.emit("unauthenticated")
            } else {
              socket.emit("authenticated")
              socket.data.isAuthenticated = true
            }
          })
          .catch((err) => console.log("cat", err.message))
      })

      socket.on("test", (payload) => {
        console.log("Got test:", payload)
        // ...
      })

      socket.emit("test2", "lol")
    })
  }
  res.end()
}

export default SocketHandler
