import { parseSlashCommand } from '../domain/slash.js'
import type { SlashExecResponse } from '../gatewayTypes.js'
import { asCommandDispatch, rpcErrorMessage } from '../lib/rpc.js'

import type { SlashHandlerContext } from './interfaces.js'
import { findSlashCommand } from './slash/registry.js'
import type { SlashRunCtx } from './slash/types.js'
import { getUiState } from './uiStore.js'

export function createSlashHandler(ctx: SlashHandlerContext): (cmd: string) => boolean {
  const { gw } = ctx.gateway
  const { catalog } = ctx.local
  const { page, send, sys } = ctx.transcript

  const handler = (cmd: string): boolean => {
    const flight = ++ctx.slashFlightRef.current
    const ui = getUiState()
    const sid = ui.sid
    const parsed = parseSlashCommand(cmd)
    const argTail = parsed.arg ? ` ${parsed.arg}` : ''

    const stale = () => flight !== ctx.slashFlightRef.current || getUiState().sid !== sid

    const guarded =
      <T>(fn: (r: T) => void) =>
      (r: null | T): void => {
        if (!stale() && r) {
          fn(r)
        }
      }

    const guardedErr = (e: unknown) => {
      if (!stale()) {
        sys(`error: ${rpcErrorMessage(e)}`)
      }
    }

    const runCtx: SlashRunCtx = { ...ctx, flight, guarded, guardedErr, sid, stale, ui }

    const found = findSlashCommand(parsed.name)

    if (found) {
      found.run(parsed.arg, runCtx, cmd)

      return true
    }

    if (catalog?.canon) {
      const needle = `/${parsed.name}`.toLowerCase()

      const matches = [
        ...new Set(
          Object.entries(catalog.canon)
            .filter(([alias]) => alias.startsWith(needle))
            .map(([, canon]) => canon)
        )
      ]

      if (matches.length === 1 && matches[0]!.toLowerCase() !== needle) {
        return handler(`${matches[0]}${argTail}`)
      }

      if (matches.length > 1) {
        sys(`ambiguous command: ${matches.slice(0, 6).join(', ')}${matches.length > 6 ? ', …' : ''}`)

        return true
      }
    }

    gw.request<SlashExecResponse>('slash.exec', { command: cmd.slice(1), session_id: sid })
      .then(r => {
        if (stale()) {
          return
        }

        const body = r?.output || `/${parsed.name}: no output`
        const text = r?.warning ? `warning: ${r.warning}\n${body}` : body
        const long = text.length > 180 || text.split('\n').filter(Boolean).length > 2

        long ? page(text, parsed.name[0]!.toUpperCase() + parsed.name.slice(1)) : sys(text)
      })
      .catch(() => {
        gw.request('command.dispatch', { arg: parsed.arg, name: parsed.name, session_id: sid })
          .then((raw: unknown) => {
            if (stale()) {
              return
            }

            const d = asCommandDispatch(raw)

            if (!d) {
              return sys('error: invalid response: command.dispatch')
            }

            if (d.type === 'exec' || d.type === 'plugin') {
              return sys(d.output || '(no output)')
            }

            if (d.type === 'alias') {
              return handler(`/${d.target}${argTail}`)
            }

            if (d.type === 'skill') {
              sys(`⚡ loading skill: ${d.name}`)

              return d.message?.trim() ? send(d.message) : sys(`/${parsed.name}: skill payload missing message`)
            }

            if (d.type === 'send') {
              return d.message?.trim() ? send(d.message) : sys(`/${parsed.name}: empty message`)
            }
          })
          .catch(guardedErr)
      })

    return true
  }

  return handler
}
