import { pikkuChannelFunc, pikkuChannelConnectionFunc, pikkuChannelDisconnectionFunc } from '#pikku'

/**
 * On WebSocket connect — greet the client with the current card list.
 * Subsequent updates are pushed by `processCardEvent` (queue worker)
 * via the eventHub.
 */
// NOTE: no generic on pikkuChannelConnectionFunc. A connect handler has no
// input message and no validated output — `channel.send` is fire-and-forget.
// Passing an output-shaped generic here makes the inspector mis-assign it as
// the connect INPUT schema, so the WS handshake (empty openingData) fails
// schema validation and the connection is rejected 403. See pikku inspector
// swap-bug: pikkuChannelConnectionFunc<Out>'s single generic is OUTPUT, but
// generic[0] is read as input.
export const onCardsConnect = pikkuChannelConnectionFunc(async ({ kysely, logger }, _, { channel }) => {
  const rows = await kysely.selectFrom('kanbanCard').selectAll().execute()
  channel.send({ type: 'hello', count: rows.length })
  logger.info(`cards-channel: connected (${rows.length} cards)`)
})

export const onCardsDisconnect = pikkuChannelDisconnectionFunc(
  async ({ logger }, _, { channel }) => {
    logger.info(`cards-channel: disconnected channel=${channel.channelId}`)
  },
)

/**
 * Echo any incoming message back to confirm the channel is bidirectional.
 * Wired via `onMessageWiring` so pikku JSON-parses the WS frame and
 * dispatches by the `action` discriminator. Send `{action:'echo', text:'…'}`
 * to receive `{echo:'…'}`.
 */
export const onCardsEcho = pikkuChannelFunc<{ text: string }, { echo: string }>(
  async (_services, { text }, { channel }) => {
    channel.send({ echo: text })
  },
)

/**
 * Channel-session round-trip handlers — used by smoke-websocket-deploy.ts to
 * verify that `setSession`/`getSession`/`clearSession` are wired up to the
 * channel store on the deployed runtime (CF: WebSocketHibernationServer DO).
 *
 * NOTE (OSS gap): pikku's current SessionService writes to
 * `sessionStore[pikkuUserId]`, which is the wrong scope for channels — channel
 * session state belongs to the channelId, not the user. This smoke is
 * intentionally kept in tree as a regression target that will start passing
 * once OSS lands channel-scoped session storage on the ChannelStore (see
 * docs/work-log.md for proposed fix).
 *
 * Handlers RETURN their reply object instead of calling `channel.send`; the
 * pikku channel runner forwards the return and stamps the `action`
 * discriminator onto it. Calling `channel.send` in addition would emit two
 * frames per message and desync any client reading one reply per request.
 */
export const onCardsSetSession = pikkuChannelFunc<
  { userId: string; label: string },
  { set: true; label: string }
>(async (_services, payload, { setSession }) => {
  await setSession({ userId: payload.userId })
  return { set: true, label: payload.label }
})

export const onCardsGetSession = pikkuChannelFunc<Record<string, never>, { session: unknown }>(
  async (_services, _input, { getSession }) => {
    const session = await getSession()
    return { session: session ?? null }
  },
)

export const onCardsClearSession = pikkuChannelFunc<Record<string, never>, { cleared: true }>(
  async (_services, _input, { clearSession }) => {
    await clearSession()
    return { cleared: true }
  },
)
