/**
 * Slash trigger plugin, browser half: the SlashService (`ctx.slash`) owning
 * trigger detection, the candidate menu, and the pick pipeline; MenuView
 * self-registers into the conversation.input.overlay slot. Frozen pipeline
 * contract in ./contract.ts; sources register through ctx.slash alone.
 */
// Type-only: pulls the locale plugin's Context merge (ctx.locale).
import type {} from '@deepseek-ai/dsh-client-locale/client'
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
import { SlashService } from './service.ts'
import type { MenuViewInjected } from './slots.ts'
import { MenuView } from './MenuView.tsx'
import { en, zh, type MenuKey } from './locales.ts'

export { SlashService } from './service.ts'
export { SlashController } from './controller.ts'
export type { SlashControllerDeps, SourceRoster } from './controller.ts'
export type { MenuViewInjected } from './slots.ts'
export type { MenuViewProps } from './MenuView.tsx'
export type { MenuKey } from './locales.ts'
export type {
  ArbitrateKey, ArbitrateOutcome, BeginCommandRequest, CandidateRequest, ClientSessionContext,
  CommandClaim, ConsumeTokenRequest, InsertReferenceRequest, PickOutcome, PickVia, ReferenceCodec,
  ReferenceInsert, SlashCandidate, SlashPick, SlashSource, SubmitOutcome, TokenSpan,
  TriggerChar, TriggerGuard, TriggerPosition,
} from '../types.ts'
export type { DetectTrigger, ExactMatch, MenuEvent, MenuReduce, MenuState, TriggerHit } from '../core/contract.ts'
export type { SlashServiceContract } from './contract.ts'

declare module 'cordis' {
  interface Context {
    /** The outward face only; the concrete service stays inside this plugin. */
    slash: import('./contract.ts').SlashServiceContract
  }
}

declare module '@deepseek-ai/dsh-client-ui-slots' {
  interface LocaleNamespaceMap {
    /** The candidate menu's copy: group titles keyed by source name, the pending row, and the listbox aria. */
    'slash.menu': MenuKey
  }
}

/** Namespace owning the candidate-menu copy. */
const MENU_NS = 'slash.menu'

/** Required services: controller resolution reads the session scope tree; the menu copy is localized. */
export const inject = ['sessions', 'locale']

/**
 * Client plugin body: mount the service, then register MenuView into the
 * input overlay once its declarer is up.
 * @param ctx - client root context.
 */
export function apply(ctx: ClientContext): void {
  ctx.plugin(SlashService)
  ctx.effect(() => ctx.locale.register(MENU_NS, { zh, en }), 'ui-slash: menu dictionaries')
  ctx.inject(['slots', 'slash', 'sessions'], (scope: ClientContext) => {
    const slash = scope.slash
    const sessions = scope.sessions
    scope.slots.inject('conversation.input.overlay', () => scope.slots.register({
      name: 'conversation.input.overlay',
      id: 'slash-menu',
      order: 0,
      locale: MENU_NS,
      inject: (sessionId): MenuViewInjected => {
        // Session-scoped slot: resolve this session's controller (the slot
        // frame hands ids, not ctx — the registered id→ctx interchange).
        const actx = sessions.scope(sessionId)
        if (actx === undefined) throw new Error(`ui-slash: session "${String(sessionId)}" resolved no scope`)
        const controller = slash.sessionOf(actx)
        return {
          menu: controller.menu,
          onPick: (source, index) => { controller.pick(source, index) },
          onDismiss: () => { controller.dismiss() },
        }
      },
    }, MenuView))
  })
}
