import type { PropType } from 'vue'
import type { DimSdkSessionListItem } from '../../keys'
import { t } from '#cli/i18n'
import { defineComponent, ref, watch } from 'vue'
import { useUiTheme } from '../theme'
import { renderSessionsDialog } from './sessions-dialog/render'
import {
  buildSessionsDialogViewModel,
  clamp,
  getSessionSearchText,
} from './sessions-dialog/view-model'

export const DimSdkSessionsDialog = defineComponent({
  name: 'DimSdkSessionsDialog',
  props: {
    modelValue: { type: Boolean, required: true },
    suspendKeyboard: { type: Boolean, default: false },
    loading: { type: Boolean, default: false },
    deleteArmed: { type: Boolean, default: false },
    cwdOnly: { type: Boolean, default: false },
    currentCwd: { type: String, default: '' },
    activeSessionId: { type: String, default: '' },
    allSessionsCount: { type: Number, default: 0 },
    sessions: {
      type: Array as PropType<readonly DimSdkSessionListItem[]>,
      required: true,
    },
    selectedIndex: { type: Number, required: true },
    w: { type: Number, default: 0 },
    h: { type: Number, default: 0 },
  },
  emits: {
    'update:modelValue': (_v: boolean) => true,
    'update:selectedIndex': (_v: number) => true,
    'update:cwdOnly': (_v: boolean) => true,
    open: () => true,
    delete: () => true,
    refresh: () => true,
  },
  setup(props, { emit }) {
    const ui = useUiTheme()
    const actionOrder = ['open', 'delete', 'scope', 'refresh', 'close'] as const
    const selectedActionIndex = ref(0)
    const focusTarget = ref<'list' | 'buttons'>('list')
    const keyHandledFlag = '__dimsdkSessionsDialogHandled'
    const typeaheadBuffer = ref('')
    let typeaheadTimer: ReturnType<typeof setTimeout> | null = null
    const TYPEAHEAD_RESET_MS = 900

    function resetTypeahead(): void {
      typeaheadBuffer.value = ''
      if (typeaheadTimer) {
        clearTimeout(typeaheadTimer)
        typeaheadTimer = null
      }
    }

    function normalizeSearchText(input: string): string {
      return input.toLocaleLowerCase().replace(/\s+/g, ' ').trim()
    }

    function findSessionMatch(query: string): number | null {
      const sessions = Array.isArray(props.sessions) ? props.sessions : []
      const total = sessions.length
      if (total <= 0) return null

      const normalizedQuery = normalizeSearchText(query)
      if (!normalizedQuery) return null

      const start = clamp(props.selectedIndex, 0, Math.max(0, total - 1))
      for (let offset = 0; offset < total; offset += 1) {
        const idx = (start + offset) % total
        const text = normalizeSearchText(getSessionSearchText(sessions[idx]!))
        if (!text) continue
        if (
          text.startsWith(normalizedQuery) ||
          text.includes(` ${normalizedQuery}`)
        )
          return idx
      }

      return null
    }

    function defaultActionIndex(): number {
      // Keep in sync with the button `default` logic below.
      return props.deleteArmed
        ? actionOrder.indexOf('delete')
        : actionOrder.indexOf('open')
    }

    function moveAction(dir: 1 | -1): void {
      const len = actionOrder.length
      if (len <= 0) {
        selectedActionIndex.value = 0
        return
      }
      selectedActionIndex.value = (selectedActionIndex.value + dir + len) % len
    }

    function confirmAction(): void {
      const action = actionOrder[selectedActionIndex.value] ?? 'open'
      if (action === 'open') emit('open')
      else if (action === 'delete') emit('delete')
      else if (action === 'scope') emit('update:cwdOnly', !props.cwdOnly)
      else if (action === 'refresh') emit('refresh')
      else emit('update:modelValue', false)
    }

    function onKeydownCapture(e: any): void {
      if (!props.modelValue) return
      if (props.suspendKeyboard) return

      if (!e || typeof e !== 'object') return

      if ((e as any)[keyHandledFlag]) return

      if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
        ;(e as any)[keyHandledFlag] = true
        e.preventDefault?.()
        e.stopPropagation?.()
        focusTarget.value = 'list'
        const max = Math.max(0, props.sessions.length - 1)
        if (max <= 0) {
          emit('update:selectedIndex', 0)
          return
        }
        const next =
          e.key === 'ArrowUp'
            ? props.selectedIndex <= 0
              ? max
              : clamp(props.selectedIndex - 1, 0, max)
            : props.selectedIndex >= max
              ? 0
              : clamp(props.selectedIndex + 1, 0, max)
        emit('update:selectedIndex', next)
        return
      }

      // Track button selection even when focus is inside TSelect (Enter is often consumed there).
      if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
        ;(e as any)[keyHandledFlag] = true
        const dir: 1 | -1 = e.key === 'ArrowLeft' ? -1 : 1
        moveAction(dir)
        focusTarget.value = 'buttons'
        return
      }

      if (e.key === 'Enter') {
        ;(e as any)[keyHandledFlag] = true
        e.preventDefault?.()
        e.stopPropagation?.()
        confirmAction()
        resetTypeahead()
        return
      }

      if (e.key === 'Backspace' && typeaheadBuffer.value) {
        ;(e as any)[keyHandledFlag] = true
        e.preventDefault?.()
        e.stopPropagation?.()
        typeaheadBuffer.value = typeaheadBuffer.value.slice(0, -1)
        const match = findSessionMatch(typeaheadBuffer.value)
        if (match !== null) emit('update:selectedIndex', match)
        return
      }

      if (
        typeof e.key === 'string' &&
        e.key.length === 1 &&
        !e.ctrlKey &&
        !e.metaKey &&
        !e.altKey
      ) {
        ;(e as any)[keyHandledFlag] = true
        e.preventDefault?.()
        e.stopPropagation?.()
        focusTarget.value = 'list'
        typeaheadBuffer.value += e.key
        if (typeaheadTimer) clearTimeout(typeaheadTimer)
        typeaheadTimer = setTimeout(() => resetTypeahead(), TYPEAHEAD_RESET_MS)
        const match = findSessionMatch(typeaheadBuffer.value)
        if (match !== null) emit('update:selectedIndex', match)
      }
    }

    watch(
      () => props.modelValue,
      (next, prev) => {
        if (next && !prev) {
          selectedActionIndex.value = defaultActionIndex()
          focusTarget.value = 'list'
          resetTypeahead()
        }
      },
      { immediate: true },
    )

    return () => {
      const vm = buildSessionsDialogViewModel({
        modelValue: props.modelValue,
        loading: props.loading,
        deleteArmed: props.deleteArmed,
        cwdOnly: props.cwdOnly,
        currentCwd: props.currentCwd,
        activeSessionId: props.activeSessionId,
        allSessionsCount: props.allSessionsCount,
        sessions: props.sessions,
        selectedIndex: props.selectedIndex,
        ...(props.w > 0 ? { w: props.w } : {}),
        ...(props.h > 0 ? { h: props.h } : {}),
      })

      return renderSessionsDialog({
        modelValue: props.modelValue,
        deleteArmed: props.deleteArmed,
        cwdOnly: props.cwdOnly,
        vm,
        ui,
        onUpdateModelValue: (v: boolean) => emit('update:modelValue', v),
        onUpdateSelectedIndex: (v: number) => emit('update:selectedIndex', v),
        onUpdateCwdOnly: (v: boolean) => emit('update:cwdOnly', v),
        onOpen: () => emit('open'),
        onDelete: () => emit('delete'),
        onRefresh: () => emit('refresh'),
        hintText: t('sessionsDialog.hint'),
        focusText: t('sessionsDialog.focusPrefix', {
          focus:
            focusTarget.value === 'buttons'
              ? t('sessionsDialog.focusButtons')
              : t('sessionsDialog.focusList'),
        }),
        onKeydownCapture: (e: any) => {
          if (props.suspendKeyboard) return
          if (e?.key === 'Escape') {
            e.preventDefault?.()
            e.stopPropagation?.()
            emit('update:modelValue', false)
            return
          }

          onKeydownCapture(e)
        },
        onListFocus: () => {
          focusTarget.value = 'list'
        },
        onListBlur: () => {
          if (focusTarget.value === 'list') focusTarget.value = 'buttons'
        },
        onConfirm: (btn: any) => {
          const v = String(btn?.value ?? '')
          const idx = actionOrder.indexOf(v as any)
          if (idx >= 0) selectedActionIndex.value = idx
          if (v === 'open') emit('open')
          else if (v === 'delete') emit('delete')
          else if (v === 'scope') emit('update:cwdOnly', !props.cwdOnly)
          else if (v === 'refresh') emit('refresh')
          else emit('update:modelValue', false)
        },
        onClose: () => emit('update:modelValue', false),
      })
    }
  },
})
