import { Hono } from 'hono'
import { randomUUID } from 'node:crypto'
import {
  getSession,
  incrementEpoch,
  touchSession,
  updateSessionStatus,
} from '../../services/session'
import {
  automationStatesEqual,
  getAutomationStateEventPayload,
} from '../../services/automationState'
import {
  apiKeyAuth,
  acceptCliHeaders,
  sessionIngressAuth,
} from '../../auth/middleware'
import { getEventBus } from '../../transport/event-bus'
import { storeGetSessionWorker, storeUpsertSessionWorker } from '../../store'

const app = new Hono()

/** GET /v1/code/sessions/:id/worker — Read worker state */
app.get('/:id/worker', acceptCliHeaders, sessionIngressAuth, async c => {
  const sessionId = c.req.param('id')!
  const session = getSession(sessionId)
  if (!session) {
    return c.json(
      { error: { type: 'not_found', message: 'Session not found' } },
      404,
    )
  }

  const worker = storeGetSessionWorker(sessionId)
  return c.json(
    {
      worker: {
        worker_status: worker?.workerStatus ?? session.status,
        external_metadata: worker?.externalMetadata ?? null,
        requires_action_details: worker?.requiresActionDetails ?? null,
        last_heartbeat_at: worker?.lastHeartbeatAt?.toISOString() ?? null,
      },
    },
    200,
  )
})

/** PUT /v1/code/sessions/:id/worker — Update worker state */
app.put('/:id/worker', acceptCliHeaders, sessionIngressAuth, async c => {
  const sessionId = c.req.param('id')!
  const session = getSession(sessionId)
  if (!session) {
    return c.json(
      { error: { type: 'not_found', message: 'Session not found' } },
      404,
    )
  }

  const body = await c.req.json()
  const prevAutomationState = getAutomationStateEventPayload(
    storeGetSessionWorker(sessionId)?.externalMetadata,
  )
  if (body.worker_status) {
    updateSessionStatus(sessionId, body.worker_status)
  } else {
    touchSession(sessionId)
  }

  const worker = storeUpsertSessionWorker(sessionId, {
    workerStatus: body.worker_status,
    externalMetadata: body.external_metadata,
    requiresActionDetails: body.requires_action_details,
  })
  const nextAutomationState = getAutomationStateEventPayload(
    worker.externalMetadata,
  )

  if (!automationStatesEqual(prevAutomationState, nextAutomationState)) {
    getEventBus(sessionId).publish({
      id: randomUUID(),
      sessionId,
      type: 'automation_state',
      payload: nextAutomationState,
      direction: 'inbound',
    })
  }

  return c.json(
    {
      status: 'ok',
      worker: {
        worker_status: worker.workerStatus ?? session.status,
        external_metadata: worker.externalMetadata,
        requires_action_details: worker.requiresActionDetails,
        last_heartbeat_at: worker.lastHeartbeatAt?.toISOString() ?? null,
      },
    },
    200,
  )
})

/** POST /v1/code/sessions/:id/worker/heartbeat — Keep worker alive */
app.post(
  '/:id/worker/heartbeat',
  acceptCliHeaders,
  sessionIngressAuth,
  async c => {
    const sessionId = c.req.param('id')!
    const session = getSession(sessionId)
    if (!session) {
      return c.json(
        { error: { type: 'not_found', message: 'Session not found' } },
        404,
      )
    }

    const now = new Date()
    storeUpsertSessionWorker(sessionId, { lastHeartbeatAt: now })
    touchSession(sessionId)
    return c.json({ status: 'ok', last_heartbeat_at: now.toISOString() }, 200)
  },
)

/** POST /v1/code/sessions/:id/worker/register — Register worker */
app.post('/:id/worker/register', acceptCliHeaders, apiKeyAuth, async c => {
  const sessionId = c.req.param('id')!
  const session = getSession(sessionId)
  if (!session) {
    return c.json(
      { error: { type: 'not_found', message: 'Session not found' } },
      404,
    )
  }

  const epoch = incrementEpoch(sessionId)
  return c.json({ worker_epoch: epoch }, 200)
})

export default app
