import websocket from '@fastify/websocket'
import { verifyAccessToken } from '../services/token.service.js'
import { subscribe, publish } from './valkey.js'
import { UserType, WsMessage } from '../constants.js'

// Track active WebSocket connections: sessionId → { customer, mitra }
const sessionConnections = new Map()

// Track user → socket mapping for FCM fallback detection
const userSockets = new Map() // `customer:${id}` or `mitra:${id}` → socket

export const registerWebSocketPlugin = async (app) => {
  await app.register(websocket)
}

export const isUserOnlineWs = (userType, userId) => {
  const key = `${userType}:${userId}`
  const socket = userSockets.get(key)
  return socket && socket.readyState === 1 // WebSocket.OPEN
}

export const getSessionConnections = (sessionId) => {
  return sessionConnections.get(sessionId) || {}
}

const sendToSocket = (socket, data) => {
  if (socket && socket.readyState === 1) {
    socket.send(JSON.stringify(data))
    return true
  }
  return false
}

export const sendToSessionParticipant = (sessionId, userType, data) => {
  const conns = sessionConnections.get(sessionId)
  if (!conns) return false
  return sendToSocket(conns[userType], data)
}

export const sendToUser = (userType, userId, data) => {
  const key = `${userType}:${userId}`
  const socket = userSockets.get(key)
  return sendToSocket(socket, data)
}

export const registerWebSocketRoute = (app) => {
  app.get('/api/shared/ws', { websocket: true }, (socket, request) => {
    let authenticatedUser = null // { type: 'customer'|'mitra', id, sessionId }
    let valkeyUnsubscribes = []

    const send = (data) => sendToSocket(socket, data)

    socket.on('message', async (raw) => {
      let msg
      try {
        msg = JSON.parse(raw.toString())
      } catch {
        send({ type: WsMessage.ERROR, message: 'Invalid JSON' })
        return
      }

      // Handle auth message
      if (msg.type === WsMessage.AUTH) {
        try {
          const claims = verifyAccessToken(msg.token)
          if (claims.userType !== UserType.CUSTOMER && claims.userType !== UserType.MITRA) {
            send({ type: WsMessage.ERROR, message: 'Unsupported user type for websocket' })
            socket.close()
            return
          }

          const userType = claims.userType
          const userId = claims.userId
          const sessionId = msg.session_id

          authenticatedUser = { type: userType, id: userId, sessionId }

          // Register in connection maps
          const userKey = `${userType}:${userId}`
          userSockets.set(userKey, socket)

          if (sessionId) {
            if (!sessionConnections.has(sessionId)) {
              sessionConnections.set(sessionId, {})
            }
            sessionConnections.get(sessionId)[userType] = socket
          }

          // Subscribe to session channel for events from other services
          if (sessionId) {
            const unsub = subscribe(`session:${sessionId}:chat`, (data) => {
              // Don't echo messages back to sender
              if (data._sender_type === userType && data._sender_id === userId) return
              const { _sender_type, _sender_id, ...payload } = data
              send(payload)
            })
            valkeyUnsubscribes.push(unsub)
          }

          send({ type: WsMessage.AUTH_OK, user_type: userType, user_id: userId })
        } catch (err) {
          send({ type: WsMessage.ERROR, message: 'Authentication failed' })
          socket.close()
        }
        return
      }

      // All other messages require authentication
      if (!authenticatedUser) {
        send({ type: WsMessage.ERROR, message: 'Not authenticated. Send auth message first.' })
        return
      }

      // Route message types to handlers via Valkey pub/sub
      const { type, ...payload } = msg
      await publish(`session:${authenticatedUser.sessionId}:incoming`, {
        type,
        ...payload,
        _sender_type: authenticatedUser.type,
        _sender_id: authenticatedUser.id,
        _session_id: authenticatedUser.sessionId,
      })
    })

    socket.on('close', () => {
      if (authenticatedUser) {
        const userKey = `${authenticatedUser.type}:${authenticatedUser.id}`
        userSockets.delete(userKey)

        if (authenticatedUser.sessionId) {
          const conns = sessionConnections.get(authenticatedUser.sessionId)
          if (conns) {
            delete conns[authenticatedUser.type]
            if (!conns[UserType.CUSTOMER] && !conns[UserType.MITRA]) {
              sessionConnections.delete(authenticatedUser.sessionId)
            }
          }
        }
      }

      // Clean up Valkey subscriptions
      for (const unsub of valkeyUnsubscribes) {
        unsub()
      }
    })
  })
}
