import fastifyPlugin from "fastify-plugin"
import { Server } from "socket.io"
import configService from "@c/config/configService"
import { getAllowedOrigins } from "@/helpers"
import { validate } from "uuid"
import { validateWorkSession } from "./socketManager"

export default fastifyPlugin((fastify, opts, done) => {
  // Get socket configuration using the wrfastifyer
  const { options: socketOptions, security } = configService.getSocketConfig()

  // Configure default options with overrides from config
  const defaults = {
    cors: { origin: getAllowedOrigins(), credentials: true },
    connectionTimeout: socketOptions?.connectionTimeout || 3000,
    pingInterval: socketOptions?.pingInterval || 1500,
    pingTimeout: socketOptions?.pingTimeout || 1000,
  }

  /*
   * create and configure the websocket server
   */
  const socketServer = new Server(fastify.server, { ...defaults, ...opts })

  // Configure authentication based on security settings
  if (configService.isAuthRequired() ?? true) {
    // Default to JWT authentication
    if (security?.tokenValidation === "jwt") {
      if (!process.env.SERVER_TOKEN_SECRET) {
        fastify.log.warn(
          "SERVER_TOKEN_SECRET not set, socket authentication may fail",
        )
      }

      // Dual authentication middleware: JWT or workSession
      socketServer.use(async (socket, next) => {
        fastify.log.debug(
          `[Socket Auth] Starting authentication for socket: ${socket.id}`,
        )
        try {
          const token =
            socket.handshake.auth?.token ??
            socket.handshake.headers.authorization?.split(" ")[1]

          // Try JWT authentication first
          if (token) {
            try {
              // Verify token using fastify-jwt
              const decoded = fastify.jwt.verify(token)

              // Store decoded token on socket
              socket.decoded_token = decoded
              socket.isWorkSession = false

              return next()
            } catch (jwtError: any) {
              // JWT verification failed, continue to workSession check
              fastify.log.debug(
                `[Socket Auth] JWT verification failed for ${socket.id}: ${jwtError.message}, checking workSession`,
              )
            }
          }

          // Check for workSession authentication (same validation as socketManager.validateWorkSession)
          const sessionId = socket.handshake.auth?.sessionId

          if (sessionId) {
            fastify.log.info(
              `[Socket Auth] Attempting workSession authentication for socket: ${socket.id}, sessionId: ${sessionId}`,
            )
            if (!validate(sessionId)) {
              fastify.log.warn(
                `[Socket Auth] Invalid sessionId format for socket: ${socket.id}`,
              )
              return next(
                new Error("Authentication failed: Invalid session ID format"),
              )
            }
            try {
              const workSession = await validateWorkSession(fastify, sessionId)
              socket.workSession = workSession as any
              socket.isWorkSession = true
              return next()
            } catch (err: any) {
              fastify.log.warn(
                `[Socket Auth] WorkSession validation failed for socket: ${socket.id}, error: ${err.message}`,
              )
              return next(new Error("Invalid or expired session"))
            }
          }

          // No valid authentication method found
          fastify.log.warn(
            `[Socket Auth] Socket connection without valid authentication: ${socket.id}, hasToken: ${!!token}, hasSessionId: ${!!sessionId}`,
          )
          return next(
            new Error(
              "Authentication failed: No valid token or session provided",
            ),
          )
        } catch (error: any) {
          fastify.log.error(
            `[Socket Auth] Socket authentication error for ${socket.id}: ${error.message}, stack: ${error.stack}`,
          )
          next(new Error("Authentication failed: Invalid token"))
        }
      })
    } else if (security?.customValidator) {
      // Custom validator would be implemented here
      fastify.log.warn(
        "Custom socket validator specified but not implemented - yet ;)",
      )
    }
  } else {
    fastify.log.warn(
      "Socket authentication is disabled - this is not recommended for production",
    )
  }

  // make the websocket server available in the whole fastify
  fastify.decorate("wss", socketServer)

  // close the websocket server when the http server is closing
  fastify.addHook("onClose", (fastify, done) => {
    fastify.log.warn("App is closing, socket connection is also closed")
    fastify.wss.close()
    done()
  })

  done()
})
