import type { Message } from '#cli/core/model'
import type { DimSdkStore } from '#cli/core/store'
import type { ComputedRef, Ref } from 'vue'
import type { DimSdkSessionListItem, DimSdkSessionsApi } from '../../../keys'
import { debugLog } from '#cli/core/debug-log'
import { t } from '#cli/i18n'
import { computed, onBeforeUnmount, watch } from 'vue'
import {
  setTerminalTitle,
  supportsTerminalTitleStack,
} from '../../../ui/terminal-title'
import { clamp } from '../../utils/math'
import { getNodeProcess } from './node-process'
import {
  computeSessionHeaderSummary,
  sanitizeSessionMetaForHeader,
} from './session-header-summary'

type UseSessionHeaderOptions = Readonly<{
  nodeLike: boolean
  store: DimSdkStore
  sessionsApi?: DimSdkSessionsApi
  sessionsList: Ref<DimSdkSessionListItem[]>
  currentSessionId: Ref<string>
  contentW: ComputedRef<number>
  input: Ref<string>
  focusMode: Ref<'input' | 'messages'>
  showRenameSession: Ref<boolean>
  showCopyToast: (text: string) => void
  refreshSessions: (opts?: {
    preferIndex?: number
    preferId?: string
  }) => Promise<void>
}>

export function useSessionHeader(opts: UseSessionHeaderOptions): Readonly<{
  sessionHeaderTitle: ComputedRef<string>
  sessionHeaderSummary: ComputedRef<string>
  renameSessionInitialValue: ComputedRef<string>
  renameSessionW: ComputedRef<number>
  renameSessionH: ComputedRef<number>
  openRenameSession: () => void
  cancelRenameSession: () => void
  applyRenameSession: (title: string) => void
}> {
  // PERF: Many header computeds depend on the full messages array (reactive),
  // but the first user message usually never changes during streaming. Cache the
  // first-user index/id so we don’t rescan O(n) on every token.
  let cachedFirstUserIndex = -1
  let cachedFirstUserId = ''
  let cachedFirstUserNoneForLength = -1
  const getFirstUserPreviewText = (): string => {
    const messages = opts.store.state.messages as Message[]
    const n = messages.length

    if (cachedFirstUserIndex >= 0 && cachedFirstUserIndex < n) {
      const m = messages[cachedFirstUserIndex]
      if (m?.role === 'user' && String(m?.id ?? '') === cachedFirstUserId)
        return String(m?.content ?? '').trim()
    }

    if (cachedFirstUserIndex < 0 && cachedFirstUserNoneForLength === n)
      return ''

    cachedFirstUserIndex = -1
    cachedFirstUserId = ''
    cachedFirstUserNoneForLength = -1

    for (let i = 0; i < n; i++) {
      const m = messages[i]
      if (m?.role === 'user') {
        cachedFirstUserIndex = i
        cachedFirstUserId = String(m?.id ?? '')
        return String(m?.content ?? '').trim()
      }
    }

    cachedFirstUserNoneForLength = n
    return ''
  }

  const currentSessionMeta = computed(() => {
    const id = opts.currentSessionId.value
    if (!id) return null

    const raw = opts.sessionsList.value.find((s) => s.id === id) ?? null
    if (!raw) return null

    // `refreshSessions()` can briefly return an empty snapshot for the current session
    // (e.g. right after session creation, before messages are persisted).
    // In that window, list-derived title/summary may be the placeholder "New session",
    // which should not override the in-memory transcript-derived preview.
    const localPreview = getFirstUserPreviewText()
    return sanitizeSessionMetaForHeader(raw, localPreview)
  })

  const sessionHeaderTitle = computed(() => {
    const id = opts.currentSessionId.value
    return `# ${id || t('chatPage.session.new')}`
  })

  const renameSessionInitialValue = computed(() => {
    const title = String(currentSessionMeta.value?.title ?? '').trim()
    return title || ''
  })

  // Use md preset width (clamp(55%cW, 48, 78))
  const renameSessionW = computed(() =>
    clamp(Math.floor(opts.contentW.value * 0.55), 48, 78),
  )
  const renameSessionH = computed(() => 9)

  function openRenameSession(): void {
    const sid = opts.currentSessionId.value
    if (!opts.sessionsApi || typeof opts.sessionsApi.rename !== 'function') {
      opts.showCopyToast(t('chatPage.toast.renameNodeOnly'))
      return
    }
    if (!sid) {
      opts.showCopyToast(t('chatPage.toast.noActiveSession'))
      return
    }
    opts.showRenameSession.value = true
  }

  function cancelRenameSession(): void {
    opts.showRenameSession.value = false
    if (
      String(opts.input.value ?? '')
        .trim()
        .startsWith('/rename')
    )
      opts.input.value = ''
    opts.focusMode.value = 'input'
  }

  function applyRenameSession(title: string): void {
    const sid = opts.currentSessionId.value
    const api = opts.sessionsApi
    if (!api || typeof api.rename !== 'function') {
      opts.showCopyToast(t('chatPage.toast.renameNodeOnly'))
      return
    }
    if (!sid) {
      opts.showCopyToast(t('chatPage.toast.noActiveSession'))
      return
    }
    const nextTitle = String(title ?? '').trim()
    if (!nextTitle) {
      opts.showCopyToast(t('chatPage.toast.sessionNameEmpty'))
      return
    }
    void (async () => {
      try {
        await api.rename!(sid, nextTitle)
        await opts.refreshSessions({ preferId: sid })
        opts.showCopyToast(
          t('chatPage.toast.sessionRenamed', { title: nextTitle }),
        )
      } catch (e) {
        debugLog('session_rename_error', {
          sessionId: sid,
          error:
            e instanceof Error
              ? { message: e.message, stack: e.stack }
              : String(e),
        })
        opts.showCopyToast(t('chatPage.toast.renameFailed'))
      }
    })()
  }

  const sessionHeaderSummary = computed(() => {
    const preview = getFirstUserPreviewText()
    return computeSessionHeaderSummary(currentSessionMeta.value, preview)
  })

  if (opts.nodeLike) {
    const proc = getNodeProcess()
    const baseTitle =
      String(proc?.env?.DIMCODE_TERM_TITLE_BASE ?? 'dim').trim() || 'dim'

    const applyTitle = (summary: string) => {
      const s = String(summary ?? '').trim()
      setTerminalTitle(s ? `${baseTitle} ${s}` : baseTitle)
    }

    watch(sessionHeaderSummary, (v) => applyTitle(v), { immediate: true })
    if (
      supportsTerminalTitleStack({
        env: proc?.env as Record<string, string | undefined> | undefined,
      })
    )
      onBeforeUnmount(() => setTerminalTitle(baseTitle))
  }

  return {
    sessionHeaderTitle,
    sessionHeaderSummary,
    renameSessionInitialValue,
    renameSessionW,
    renameSessionH,
    openRenameSession,
    cancelRenameSession,
    applyRenameSession,
  }
}
