import { NOTIFICATION_EVENTS } from '../../../shared/constants/notification-events.js'

const {
  ORDER_CREATED,
  ORDER_STATUS_CHANGED,
  ORDER_MESSAGE_SENT,
  ORDER_MESSAGE_ADMIN_REPLY,
  PAYMENT_STATUS_CHANGED,
  DELIVERY_FEE_ADJUSTED,
} = NOTIFICATION_EVENTS

export function isAdminUser(user) {
  return !!(process.env.ADMIN_EMAIL && user?.email === process.env.ADMIN_EMAIL)
}

export function formatSSE(event, data) {
  const lines = [`event: ${event}`]
  if (data !== undefined) {
    lines.push(`data: ${JSON.stringify(data)}`)
  }
  return lines.join('\n') + '\n\n'
}

export function formatHeartbit() {
  return ':heartbit\n\n'
}

export function buildSseListeners(userId, admin, eventBus, write) {
  const listeners = []

  function on(eventName, filterFn, sseEvent, dataFn) {
    function handler(payload) {
      if (!filterFn(payload)) return
      write(formatSSE(sseEvent, dataFn(payload)))
    }
    listeners.push({ eventName, handler })
    eventBus.on(eventName, handler)
  }

  on(
    ORDER_MESSAGE_ADMIN_REPLY,
    (p) => p.userId === userId,
    'message:new',
    (p) => ({ orderId: p.orderId, messageId: p.messageId, preview: p.preview }),
  )

  on(
    ORDER_MESSAGE_SENT,
    () => admin,
    'message:new',
    (p) => ({ orderId: p.orderId, messageId: p.messageId, preview: p.preview }),
  )

  on(
    ORDER_STATUS_CHANGED,
    (p) => p.userId === userId,
    'order:statusChanged',
    (p) => ({ orderId: p.orderId, newStatus: p.newStatus }),
  )

  on(
    PAYMENT_STATUS_CHANGED,
    (p) => p.userId === userId,
    'order:statusChanged',
    (p) => ({ orderId: p.orderId }),
  )

  on(
    DELIVERY_FEE_ADJUSTED,
    (p) => p.userId === userId,
    'order:updated',
    (p) => ({ orderId: p.orderId }),
  )

  on(
    ORDER_CREATED,
    () => admin,
    'order:new',
    (p) => ({ orderId: p.orderId }),
  )

  on(
    'order:created:admin',
    () => admin,
    'order:new',
    (p) => ({ orderId: p.orderId }),
  )

  return function cleanup() {
    for (const { eventName, handler } of listeners) {
      eventBus.off(eventName, handler)
    }
  }
}

export async function registerSseRoutes(fastify) {
  fastify.get('/api/sse/stream', { preHandler: [fastify.authenticate] }, async (request, reply) => {
    reply.hijack()

    reply.raw.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
      'X-Accel-Buffering': 'no',
    })

    let closed = false
    let heartbitTimer
    let removeListeners

    function cleanUp() {
      if (closed) return
      closed = true
      clearInterval(heartbitTimer)
      removeListeners()
    }

    function safeWrite(chunk) {
      if (closed) return
      try {
        reply.raw.write(chunk)
      } catch (err) {
        request.log.error({ err }, '[sse] safeWrite failed')
        closed = true
        cleanUp()
      }
    }

    const userId = request.user.sub
    const admin = isAdminUser(request.user)

    safeWrite(formatHeartbit())

    heartbitTimer = setInterval(() => {
      safeWrite(formatHeartbit())
    }, 30_000)

    removeListeners = buildSseListeners(userId, admin, fastify.eventBus, safeWrite)

    request.raw.on('close', cleanUp)
    request.raw.on('error', cleanUp)
  })
}
