<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
import type {
  ParsedConversation,
  ParsedUserMessage,
  ParsedAssistantMessage,
  ParsedSystemMessage,
  ConversationMessage,
  AgentName,
  SectionSummary
} from '@/types/transcript-debug'
import type { EphemeralTerminal } from '@/composables/useEphemeralTerminal'
import { useSessionState, type AgentStatus } from '@/stores/session-state'
import UserMessageBubble from './UserMessageBubble.vue'
import AssistantMessageBubble from './AssistantMessageBubble.vue'
import ProgressEvent from './ProgressEvent.vue'
import SystemMessage from './SystemMessage.vue'
import TurnEndDivider from './TurnEndDivider.vue'
import UserInput from './UserInput.vue'
import SessionLifecycleStatus from './SessionLifecycleStatus.vue'
import ResumeTerminalButton from './ResumeTerminalButton.vue'

const props = defineProps<{
  conversation: ParsedConversation
  processing?: boolean
  terminalReady?: boolean | null
  terminal?: EphemeralTerminal | null
  showSelector?: boolean
  agents?: { id: AgentName; label: string }[]
  selectedAgent?: AgentName | null
  sessions?: { id: string; firstUserMessage?: string }[]
  selectedSessionId?: string | null
  sessionsLoading?: boolean
  voiceMode?: 'web' | 'whisper'
  whisperStatus?: 'offline' | 'loading' | 'ready'
  audioDevices?: MediaDeviceInfo[]
  selectedDeviceId?: string
  isRecording?: boolean
  voiceTranscript?: string
  lastAudioUrl?: string
  isPlayingAudio?: boolean
  overlayOpacity?: number
  inputMaxLines?: number
  scrollJumpPercent?: number
  scrollNavMode?: 'scrollbar' | 'buttons' | 'none'
  hookPermissionMode?: string
}>()

// ── Agent status display ──
const sessionStore = useSessionState()

const STATUS_DISPLAY: Record<AgentStatus, { color: string; label: string }> = {
  idle:              { color: '#6b7280', label: 'Available' },
  thinking:          { color: '#60a5fa', label: 'Thinking...' },
  reading:           { color: '#22d3ee', label: 'Reading' },
  writing:           { color: '#4ade80', label: 'Writing' },
  toolUse:           { color: '#fbbf24', label: 'Using' },
  permissionRequest: { color: '#fb923c', label: 'Waiting approval' },
  interrupted:       { color: '#f87171', label: 'Interrupted' },
  error:             { color: '#f87171', label: 'Error' },
  sessionStart:      { color: '#60a5fa', label: 'Starting' },
  sessionEnd:        { color: '#6b7280', label: 'Session ended' },
}

const agentStatus = computed(() => {
  const agent = props.selectedAgent
  if (!agent) return null
  const state = sessionStore.agents[agent]
  if (!state) return null
  const display = STATUS_DISPLAY[state.status] || STATUS_DISPLAY.idle
  const toolSuffix = state.currentTool ? ` ${state.currentTool.name}` : ''
  return {
    ...display,
    label: display.label + toolSuffix,
    status: state.status,
  }
})

// ── Lifecycle hook event (for SessionLifecycleStatus) ──
const lifecycleEvent = computed(() => {
  const agent = props.selectedAgent
  if (!agent) return null
  return sessionStore.agents[agent]?.lastHookEvent ?? null
})

const lifecycleDetail = computed(() => {
  const agent = props.selectedAgent
  if (!agent) return ''
  return sessionStore.agents[agent]?.lastHookDetail ?? ''
})

const hookHistory = computed(() => {
  const agent = props.selectedAgent
  if (!agent) return []
  return sessionStore.agents[agent]?.hookHistory ?? []
})

// ── Derived display values ──
const permissionMode = computed(() => props.hookPermissionMode || '')
const fullCwd = computed(() => props.conversation.metadata.cwd || '')
const displayCwd = computed(() => {
  const cwd = fullCwd.value
  if (!cwd) return ''
  const parts = cwd.replace(/\\/g, '/').split('/').filter(Boolean)
  return parts.length > 0 ? parts[parts.length - 1] : cwd
})

const emit = defineEmits<{
  send: [message: string]
  switchAgent: [agent: AgentName]
  selectSession: [sessionId: string]
  createSession: []
  closeSession: [sessionId: string]
  startRecording: []
  stopRecording: []
  setVoiceMode: [mode: 'web' | 'whisper']
  selectMicrophone: [deviceId: string]
  playLastAudio: []
  'update:overlayOpacity': [value: number]
  'update:inputMaxLines': [value: number]
  'update:scrollJumpPercent': [value: number]
  'update:scrollNavMode': [value: 'scrollbar' | 'buttons' | 'none']
}>()

const scrollContainer = ref<HTMLElement | null>(null)

// ── Clipboard for session ID ──
const idCopied = ref(false)
async function copySessionId() {
  await navigator.clipboard.writeText(props.conversation.sessionId)
  idCopied.value = true
  setTimeout(() => (idCopied.value = false), 1500)
}

// ── Terminal control keys ──
function sendCtrlM() {
  props.terminal?.sendRaw('\x0d')
}

function sendEsc() {
  props.terminal?.sendRaw('\x1b')
}

// ── Close session confirm ──
const closeConfirm = ref(false)
let closeConfirmTimer: ReturnType<typeof setTimeout> | null = null

function handleCloseSession() {
  if (!closeConfirm.value) {
    closeConfirm.value = true
    closeConfirmTimer = setTimeout(() => { closeConfirm.value = false }, 3000)
    return
  }
  if (closeConfirmTimer) clearTimeout(closeConfirmTimer)
  closeConfirm.value = false
  emit('closeSession', props.conversation.sessionId)
}

// ── Multi-select ──
const selectMode = ref(false)
const selectedUuids = ref(new Set<string>())
const hasSelection = computed(() => selectedUuids.value.size > 0)

function toggleSelectMode() {
  selectMode.value = !selectMode.value
  if (!selectMode.value) selectedUuids.value = new Set()
}

function toggleSelect(uuid: string) {
  if (!selectMode.value) return
  const s = new Set(selectedUuids.value)
  if (s.has(uuid)) s.delete(uuid)
  else s.add(uuid)
  selectedUuids.value = s
}

const selectableUuids = computed(() =>
  props.conversation.messages.filter(m => m.kind !== 'progress').map(m => m.uuid)
)
const allSelected = computed(() =>
  selectableUuids.value.length > 0 && selectableUuids.value.every(u => selectedUuids.value.has(u))
)

function toggleSelectAll() {
  if (allSelected.value) {
    selectedUuids.value = new Set()
  } else {
    selectedUuids.value = new Set(selectableUuids.value)
  }
}

function clearSelection() {
  selectedUuids.value = new Set()
}

function messageToText(msg: ConversationMessage): string {
  switch (msg.kind) {
    case 'user': {
      const u = msg as ParsedUserMessage
      return `[User] ${u.content}`
    }
    case 'assistant': {
      const a = msg as ParsedAssistantMessage
      const parts: string[] = []
      for (const t of a.textBlocks) {
        if (t.trim()) parts.push(t)
      }
      for (const tc of a.toolCalls) {
        parts.push(`[Tool: ${tc.name}] ${JSON.stringify(tc.input)}`)
        if (tc.result) {
          parts.push(`[Result${tc.result.isError ? ' ERROR' : ''}] ${tc.result.content}`)
        }
      }
      return `[Assistant] ${parts.join('\n')}`
    }
    case 'system': {
      const s = msg as ParsedSystemMessage
      return `[System] ${s.content}`
    }
    default:
      return ''
  }
}

const selectedCopied = ref(false)
async function copySelected() {
  const ordered = props.conversation.messages.filter(m => selectedUuids.value.has(m.uuid))
  const text = ordered.map(messageToText).filter(Boolean).join('\n\n')
  await navigator.clipboard.writeText(text)
  selectedCopied.value = true
  setTimeout(() => (selectedCopied.value = false), 1500)
}

// ── Collapse sections ──
const collapsedSections = ref(new Set<string>())

// Special user messages (interrupted, meta) are NOT section leaders —
// they belong to the previous normal user message's section.
function isSpecialUserMessage(msg: ConversationMessage): boolean {
  if (msg.kind !== 'user') return false
  if (msg.isMeta) return true
  if (msg.content?.includes('[Request interrupted by user')) return true
  return false
}

// For each message, find which user message "owns" it (section leader)
const sectionMap = computed(() => {
  const map = new Map<string, string>() // messageUuid → ownerUserUuid
  let currentUserUuid: string | null = null
  for (const msg of props.conversation.messages) {
    if (msg.kind === 'user' && !isSpecialUserMessage(msg)) {
      currentUserUuid = msg.uuid
    } else if (currentUserUuid) {
      map.set(msg.uuid, currentUserUuid)
    }
  }
  return map
})

// Rich summary of non-leader messages per section
const sectionSummaries = computed(() => {
  const summaries = new Map<string, SectionSummary>()
  let currentUserUuid: string | null = null

  function getOrCreate(uuid: string): SectionSummary {
    if (!summaries.has(uuid)) {
      summaries.set(uuid, {
        total: 0, assistantCount: 0, systemCount: 0, progressCount: 0,
        toolNames: [], hasErrors: false, errorCount: 0,
        inputTokens: 0, outputTokens: 0
      })
    }
    return summaries.get(uuid)!
  }

  const toolSets = new Map<string, Set<string>>()

  for (const msg of props.conversation.messages) {
    if (msg.kind === 'user' && !isSpecialUserMessage(msg)) {
      currentUserUuid = msg.uuid
      getOrCreate(currentUserUuid)
      if (!toolSets.has(currentUserUuid)) toolSets.set(currentUserUuid, new Set())
    } else if (currentUserUuid) {
      const s = getOrCreate(currentUserUuid)
      const ts = toolSets.get(currentUserUuid)!
      s.total++

      if (msg.kind === 'assistant') {
        const a = msg as ParsedAssistantMessage
        s.assistantCount++
        if (a.usage) {
          s.inputTokens += a.usage.input_tokens || 0
          s.outputTokens += a.usage.output_tokens || 0
        }
        for (const tc of a.toolCalls) {
          ts.add(tc.name)
          if (tc.result?.isError) {
            s.hasErrors = true
            s.errorCount++
          }
        }
      } else if (msg.kind === 'system') {
        s.systemCount++
      } else if (msg.kind === 'progress') {
        s.progressCount++
      }
    }
  }

  // Flatten tool sets into sorted arrays
  for (const [uuid, ts] of toolSets) {
    const s = summaries.get(uuid)
    if (s) s.toolNames = [...ts].sort()
  }

  return summaries
})

// Keep simple counts for v-if checks
const sectionCounts = computed(() => {
  const counts = new Map<string, number>()
  for (const [uuid, s] of sectionSummaries.value) {
    counts.set(uuid, s.total)
  }
  return counts
})

function toggleCollapse(userUuid: string) {
  const s = new Set(collapsedSections.value)
  if (s.has(userUuid)) s.delete(userUuid)
  else s.add(userUuid)
  collapsedSections.value = s
}

function isCollapsedChild(msg: { uuid: string; kind: string }): boolean {
  // Special user messages collapse with their section like any other child
  if (msg.kind === 'user' && !isSpecialUserMessage(msg as ConversationMessage)) return false
  const owner = sectionMap.value.get(msg.uuid)
  return !!owner && collapsedSections.value.has(owner)
}

// Collapse all user sections except the last one (only normal user messages)
const userUuids = computed(() =>
  props.conversation.messages
    .filter(m => m.kind === 'user' && !isSpecialUserMessage(m))
    .map(m => m.uuid)
)

const autoCollapseEnabled = ref(false)

const allCollapsed = computed(() => {
  const uuids = userUuids.value
  if (uuids.length <= 1) return false
  const toCollapse = uuids.slice(0, -1)
  return toCollapse.length > 0 && toCollapse.every(u => collapsedSections.value.has(u))
})

function collapseAllExceptLast() {
  const uuids = userUuids.value
  if (uuids.length <= 1) return
  if (allCollapsed.value) {
    collapsedSections.value = new Set()
    autoCollapseEnabled.value = false
  } else {
    collapsedSections.value = new Set(uuids.slice(0, -1))
    autoCollapseEnabled.value = true
  }
}

// Auto-collapse: when enabled, collapse new sections as they appear
watch(userUuids, (newUuids) => {
  if (!autoCollapseEnabled.value) return
  if (newUuids.length <= 1) return
  collapsedSections.value = new Set(newUuids.slice(0, -1))
})

defineExpose({ selectMode, toggleSelectMode, allCollapsed, collapseAllExceptLast })

// Track messages that just resolved from optimistic → real
// These skip the bounce animation and get a smooth transition instead
const resolvedUuids = ref(new Set<string>())

// Scroll to bottom when a new transcript is loaded
watch(
  () => props.conversation.sessionId,
  async () => {
    await nextTick()
    if (scrollContainer.value) {
      scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight
    }
  },
  { immediate: true }
)

// Auto-scroll to bottom when messages change
watch(
  () => props.conversation.messages.length,
  async () => {
    await nextTick()
    if (scrollContainer.value) {
      scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight
    }
  }
)

// Watch for optimistic messages disappearing
watch(
  () => props.conversation.messages.map(m => m.uuid).join(','),
  (newKeys, oldKeys) => {
    if (!oldKeys) return
    const oldHadOptimistic = oldKeys.includes('optimistic-')
    const newHasOptimistic = newKeys.includes('optimistic-')

    if (oldHadOptimistic && !newHasOptimistic) {
      // Optimistic just got replaced — mark new user messages as resolved
      const oldSet = new Set(oldKeys.split(','))
      const newUuids = newKeys.split(',').filter(k => !oldSet.has(k))
      for (const uuid of newUuids) {
        resolvedUuids.value.add(uuid)
      }
      // Clear after animation
      setTimeout(() => resolvedUuids.value.clear(), 400)
    }
  }
)

function formatDuration(start: string, end: string): string {
  if (!start || !end) return ''
  const ms = new Date(end).getTime() - new Date(start).getTime()
  const s = Math.floor(ms / 1000)
  if (s < 60) return `${s}s`
  const m = Math.floor(s / 60)
  const rs = s % 60
  return `${m}m ${rs}s`
}
</script>

<template>
  <div class="chat-container">
    <!-- Agent/Session selector header (only visible via settings toggle) -->
    <div class="chat-header">
      <div class="selector-row">
        <label class="selector-label">Agent</label>
        <div class="agent-selector" v-if="agents">
          <button
            v-for="a in agents"
            :key="a.id"
            :class="['agent-btn', { active: selectedAgent === a.id }]"
            @click="emit('switchAgent', a.id)"
          >
            {{ a.label }}
          </button>
        </div>
      </div>
      <div class="selector-row">
        <label class="selector-label">Session</label>
        <select
          class="session-select"
          :value="selectedSessionId || ''"
          @change="emit('selectSession', ($event.target as HTMLSelectElement).value)"
          :disabled="sessionsLoading"
          v-if="sessions"
        >
          <option value="" disabled>Select session...</option>
          <option v-for="s in sessions" :key="s.id" :value="s.id">
            {{ s.firstUserMessage ? (s.firstUserMessage.length > 50 ? s.firstUserMessage.slice(0, 50) + '...' : s.firstUserMessage) : s.id.slice(0, 8) + '...' }}
          </option>
        </select>
        <button class="new-session-btn" @click="emit('createSession')" title="Start new session">
          <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
            <line x1="12" y1="5" x2="12" y2="19"/>
            <line x1="5" y1="12" x2="19" y2="12"/>
          </svg>
          <span>New</span>
        </button>
        <span v-if="sessionsLoading" class="spinner-sm"></span>
      </div>
      <div class="selector-row">
        <label class="selector-label">ID</label>
        <span class="status-id" :title="conversation.sessionId">{{ conversation.sessionId.slice(0, 8) }}…</span>
        <button class="copy-id-btn" :class="{ copied: idCopied }" @click="copySessionId" title="Copy session ID">
          <svg v-if="!idCopied" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
          </svg>
          <svg v-else width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <polyline points="20 6 9 17 4 12"/>
          </svg>
        </button>
        <ResumeTerminalButton
          v-if="selectedAgent"
          :agent="selectedAgent"
          :session-id="conversation.sessionId"
          :terminal="terminal ?? null"
        />
      </div>
      <div v-if="voiceMode" class="selector-row">
        <label class="selector-label">Mic</label>
        <select
          class="session-select"
          :value="selectedDeviceId || ''"
          @change="emit('selectMicrophone', ($event.target as HTMLSelectElement).value)"
        >
          <option value="" disabled>Select mic...</option>
          <option v-for="d in audioDevices" :key="d.deviceId" :value="d.deviceId">
            {{ d.label || 'Microphone ' + d.deviceId.slice(0, 8) }}
          </option>
        </select>
        <button
          class="mode-toggle-btn"
          :class="{ gpu: voiceMode === 'whisper' }"
          @click="emit('setVoiceMode', voiceMode === 'whisper' ? 'web' : 'whisper')"
          :title="voiceMode === 'whisper' ? 'Switch to Web Speech' : 'Switch to Whisper GPU'"
        >
          {{ voiceMode === 'whisper' ? 'GPU' : 'WEB' }}
        </button>
        <span
          class="whisper-dot"
          :class="whisperStatus"
          :title="'Whisper: ' + (whisperStatus || 'offline')"
        ></span>
        <button
          v-if="lastAudioUrl"
          class="play-audio-btn"
          :class="{ playing: isPlayingAudio }"
          @click="emit('playLastAudio')"
          title="Play last recording"
        >
          <svg v-if="!isPlayingAudio" width="10" height="10" viewBox="0 0 24 24" fill="currentColor">
            <polygon points="5 3 19 12 5 21 5 3"/>
          </svg>
          <svg v-else width="10" height="10" viewBox="0 0 24 24" fill="currentColor">
            <rect x="6" y="4" width="4" height="16"/>
            <rect x="14" y="4" width="4" height="16"/>
          </svg>
        </button>
      </div>
      <div class="selector-row">
        <label class="selector-label">Overlay</label>
        <input
          type="range"
          class="overlay-slider"
          min="0"
          max="100"
          :value="Math.round((overlayOpacity ?? 0.55) * 100)"
          @input="emit('update:overlayOpacity', +($event.target as HTMLInputElement).value / 100)"
        />
        <span class="overlay-value">{{ Math.round((overlayOpacity ?? 0.55) * 100) }}%</span>
      </div>
      <div class="selector-row">
        <label class="selector-label">Lines</label>
        <input
          type="range"
          class="overlay-slider"
          min="1"
          max="12"
          :value="inputMaxLines ?? 6"
          @input="emit('update:inputMaxLines', +($event.target as HTMLInputElement).value)"
        />
        <span class="overlay-value">{{ inputMaxLines ?? 6 }}</span>
      </div>
      <div class="selector-row">
        <label class="selector-label">Nav</label>
        <div class="agent-selector">
          <button
            v-for="mode in (['scrollbar', 'buttons', 'none'] as const)"
            :key="mode"
            :class="['agent-btn', { active: (scrollNavMode ?? 'buttons') === mode }]"
            @click="emit('update:scrollNavMode', mode)"
          >{{ mode }}</button>
        </div>
      </div>
      <div v-if="(scrollNavMode ?? 'buttons') === 'buttons'" class="selector-row">
        <label class="selector-label">Jump</label>
        <input
          type="range"
          class="overlay-slider"
          min="10"
          max="100"
          step="5"
          :value="scrollJumpPercent ?? 50"
          @input="emit('update:scrollJumpPercent', +($event.target as HTMLInputElement).value)"
        />
        <span class="overlay-value">{{ scrollJumpPercent ?? 50 }}%</span>
      </div>
    </div>
    <div ref="scrollContainer" class="messages-scroll">
      <template
        v-for="(msg, idx) in conversation.messages"
        :key="msg.uuid"
      >
        <div
          v-if="!isCollapsedChild(msg)"
          :class="['message-wrapper', {
            resolved: resolvedUuids.has(msg.uuid),
            selected: selectedUuids.has(msg.uuid),
            'assistant-continuation': msg.kind === 'assistant' && idx > 0 && conversation.messages[idx - 1].kind === 'assistant' && !isCollapsedChild(conversation.messages[idx - 1])
          }]"
        >
          <!-- Select checkbox -->
          <button
            v-if="selectMode && msg.kind !== 'progress'"
            :class="['select-checkbox', { checked: selectedUuids.has(msg.uuid) }]"
            @click.stop="toggleSelect(msg.uuid)"
            :title="selectedUuids.has(msg.uuid) ? 'Deselect' : 'Select'"
          >
            <svg v-if="selectedUuids.has(msg.uuid)" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
              <polyline points="20 6 9 17 4 12"/>
            </svg>
          </button>

          <div :class="['message-content', { selectable: selectMode && msg.kind !== 'progress' }]" @click="selectMode && msg.kind !== 'progress' && toggleSelect(msg.uuid)">
            <UserMessageBubble
              v-if="msg.kind === 'user'"
              :message="msg"
              :collapsed="collapsedSections.has(msg.uuid)"
              :section-count="sectionCounts.get(msg.uuid) || 0"
              :section-summary="sectionSummaries.get(msg.uuid)"
              @toggle-collapse="toggleCollapse(msg.uuid)"
            />
            <AssistantMessageBubble
              v-else-if="msg.kind === 'assistant'"
              :message="msg"
              :show-header="!(idx > 0 && conversation.messages[idx - 1].kind === 'assistant')"
            />
            <ProgressEvent
              v-else-if="msg.kind === 'progress'"
              :group="msg"
            />
            <TurnEndDivider
              v-else-if="msg.kind === 'system' && msg.subtype === 'turn_end'"
              :message="msg"
            />
            <SystemMessage
              v-else-if="msg.kind === 'system'"
              :message="msg"
            />
          </div>
        </div>
      </template>
    </div>

    <!-- Selection floating bar -->
    <Transition name="slide-up">
      <div v-if="selectMode" class="selection-bar">
        <button class="selection-btn toggle-all" @click="toggleSelectAll">
          {{ allSelected ? 'Deselect all' : 'Select all' }}
        </button>
        <span class="selection-count">{{ selectedUuids.size }} selected</span>
        <button v-if="hasSelection" class="selection-btn copy" @click="copySelected">
          <svg v-if="!selectedCopied" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
          </svg>
          <svg v-else width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <polyline points="20 6 9 17 4 12"/>
          </svg>
          {{ selectedCopied ? 'Copied!' : 'Copy' }}
        </button>
        <button v-if="hasSelection" class="selection-btn cancel" @click="clearSelection" title="Clear selection">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <line x1="18" y1="6" x2="6" y2="18"/>
            <line x1="6" y1="6" x2="18" y2="18"/>
          </svg>
        </button>
      </div>
    </Transition>

    <div class="bottom-overlay">
      <SessionLifecycleStatus
        :current-event="lifecycleEvent"
        :event-detail="lifecycleDetail"
        :hook-history="hookHistory"
      />

      <UserInput
        :processing="props.processing"
        :terminal-ready="props.terminalReady"
        :voice-transcript="voiceTranscript"
        :is-recording="isRecording"
        :voice-mode="voiceMode"
        :whisper-status="whisperStatus"
        :max-lines="props.inputMaxLines"
        @send="emit('send', $event)"
        @start-recording="emit('startRecording')"
        @stop-recording="emit('stopRecording')"
      />

      <div class="status-bar">
      <span v-if="permissionMode" class="meta-badge mode">{{ permissionMode }}</span>
      <span v-if="displayCwd" class="meta-badge origin" tabindex="0">{{ fullCwd }}</span>
      <span class="meta-count">{{ conversation.messages.length }} msgs</span>
      <button class="new-session-status-btn" @click="emit('createSession')" title="New session">
        <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
          <line x1="12" y1="5" x2="12" y2="19"/>
          <line x1="5" y1="12" x2="19" y2="12"/>
        </svg>
      </button>
      <button class="key-btn" @click="sendEsc" title="Esc" :disabled="!terminal">
        <span class="key-label">Esc</span>
      </button>
      <button class="key-btn" @click="sendCtrlM" title="Ctrl+M (Enter)" :disabled="!terminal">
        <span class="key-label">C-m</span>
      </button>
      <ResumeTerminalButton
        v-if="selectedAgent"
        :agent="selectedAgent"
        :session-id="conversation.sessionId"
        :terminal="terminal ?? null"
      />
      <span v-if="conversation.metadata.startTime && conversation.metadata.endTime" class="meta-duration">
        {{ formatDuration(conversation.metadata.startTime, conversation.metadata.endTime) }}
      </span>
      <span class="status-spacer" />
      <button
        :class="['close-session-btn', { confirming: closeConfirm }]"
        @click="handleCloseSession"
        :title="closeConfirm ? 'Click again to confirm' : 'Close session'"
      >
        <svg v-if="!closeConfirm" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <line x1="18" y1="6" x2="6" y2="18"/>
          <line x1="6" y1="6" x2="18" y2="18"/>
        </svg>
        <template v-else>
          <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <line x1="18" y1="6" x2="6" y2="18"/>
            <line x1="6" y1="6" x2="18" y2="18"/>
          </svg>
          <span class="confirm-label">confirm?</span>
        </template>
      </button>
    </div>
    </div>
  </div>
</template>

<style scoped>
.chat-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--bg-primary);
  border: 1px solid var(--border-color);
  border-radius: 8px;
  overflow: hidden;
  position: relative;
}

.chat-header {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0.4rem 0.75rem;
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--border-color);
  flex-shrink: 0;
}

.header-spacer {
  flex: 1;
}

.select-mode-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--border-color);
  border-radius: 5px;
  background: transparent;
  cursor: pointer;
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
  transition: all 0.15s;
  flex-shrink: 0;
}

.select-mode-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.select-mode-btn.active {
  background: var(--accent, #6366f1);
  border-color: var(--accent, #6366f1);
  color: white;
}

.select-mode-label {
  white-space: nowrap;
}

.copy-id-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border: none;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: all 0.15s;
}

.copy-id-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.copy-id-btn.copied {
  color: #22c55e;
}

/* ── Bottom overlay (lifecycle + input + status bar) ── */
.bottom-overlay {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}

/* ── Status bar (below input) ── */
.status-bar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0 0.75rem 0.3rem;
  background: var(--bg-secondary);
  flex-shrink: 0;
}

.new-session-status-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border: none;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: all 0.15s;
}

.new-session-status-btn:hover {
  background: var(--bg-hover);
  color: var(--accent, #6366f1);
}

.key-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 16px;
  padding: 0 0.25rem;
  border: none;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: all 0.15s;
}

.key-btn:hover:not(:disabled) {
  background: rgba(251, 191, 36, 0.15);
  color: #fbbf24;
}

.key-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.key-label {
  font-size: 8px;
  font-weight: 600;
  font-family: 'Courier New', monospace;
  letter-spacing: 0.02em;
}

.status-spacer {
  flex: 1;
}

.close-session-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  width: 16px;
  height: 16px;
  border: none;
  background: transparent;
  border-radius: 3px;
  cursor: pointer;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: all 0.15s;
}

.close-session-btn:hover {
  background: rgba(239, 68, 68, 0.1);
  color: #f87171;
}

.close-session-btn.confirming {
  width: auto;
  padding: 0 0.35rem;
  background: rgba(239, 68, 68, 0.15);
  border: 1px solid rgba(239, 68, 68, 0.3);
  color: #f87171;
}

.close-session-btn.confirming:hover {
  background: rgba(239, 68, 68, 0.25);
}

.confirm-label {
  font-size: 9px;
  font-weight: 600;
  font-family: 'Courier New', monospace;
  white-space: nowrap;
}

.status-id {
  font-size: 9px;
  font-weight: 600;
  color: var(--text-muted);
  font-family: 'Courier New', monospace;
  letter-spacing: 0.3px;
  white-space: nowrap;
}

.meta-badge {
  font-size: 9px;
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  font-family: 'Courier New', monospace;
  white-space: nowrap;
}

.meta-badge.mode {
  background: rgba(34, 197, 94, 0.12);
  color: #86efac;
  text-transform: lowercase;
}

.meta-badge.origin {
  background: rgba(99, 102, 241, 0.1);
  color: var(--accent, #6366f1);
  max-width: 80px;
  overflow: hidden;
  text-overflow: ellipsis;
  direction: rtl;
  text-align: left;
  cursor: default;
  transition: max-width 0.35s ease;
  outline: none;
}

.meta-badge.origin:hover,
.meta-badge.origin:focus {
  max-width: 400px;
  direction: ltr;
  background: rgba(99, 102, 241, 0.18);
}

.meta-duration {
  font-size: 9px;
  color: var(--text-muted);
  font-family: 'Courier New', monospace;
  margin-left: auto;
}

.meta-count {
  font-size: 9px;
  color: var(--text-muted);
  font-family: 'Courier New', monospace;
}

.messages-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.message-wrapper {
  animation: fadeIn 0.15s ease-out;
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
  position: relative;
}

.message-wrapper.selected {
  background: rgba(99, 102, 241, 0.06);
  border-radius: 8px;
  margin: -0.25rem -0.4rem;
  padding: 0.25rem 0.4rem;
}

/* Consecutive assistant messages: tighter spacing */
.message-wrapper.assistant-continuation {
  margin-top: -0.5rem;
}

/* Messages that replaced an optimistic one: smooth settle instead of bounce */
.message-wrapper.resolved {
  animation: resolveIn 0.3s ease-out;
}

.message-content {
  flex: 1;
  min-width: 0;
}

.message-content.selectable {
  cursor: pointer;
}

/* ── Select checkbox ── */
.select-checkbox {
  width: 20px;
  height: 20px;
  min-width: 20px;
  border: 1.5px solid var(--border-color);
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 0.6rem;
  transition: all 0.15s;
  color: transparent;
  flex-shrink: 0;
}

.select-checkbox:hover {
  border-color: var(--accent, #6366f1);
  background: rgba(99, 102, 241, 0.06);
}

.select-checkbox.checked {
  background: var(--accent, #6366f1);
  border-color: var(--accent, #6366f1);
  color: white;
}

/* ── Selection floating bar ── */
.selection-bar {
  position: absolute;
  bottom: 4.5rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  background: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  z-index: 10;
}

.selection-count {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
  padding: 0 0.25rem;
}

.selection-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.6rem;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 500;
  transition: all 0.15s;
}

.selection-btn.toggle-all {
  background: var(--bg-hover);
  color: var(--text-secondary);
}

.selection-btn.toggle-all:hover {
  color: var(--text-primary);
  filter: brightness(1.1);
}

.selection-btn.copy {
  background: var(--accent, #6366f1);
  color: white;
}

.selection-btn.copy:hover {
  filter: brightness(1.1);
}

.selection-btn.cancel {
  background: transparent;
  color: var(--text-muted);
  padding: 0.3rem;
}

.selection-btn.cancel:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

/* ── Slide-up transition ── */
.slide-up-enter-active,
.slide-up-leave-active {
  transition: all 0.2s ease;
}

.slide-up-enter-from,
.slide-up-leave-to {
  opacity: 0;
  transform: translateX(-50%) translateY(8px);
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes resolveIn {
  from { opacity: 0.7; }
  to { opacity: 1; }
}

.selector-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.selector-label {
  font-size: 9px;
  font-weight: 700;
  font-family: 'Courier New', monospace;
  color: var(--text-muted, rgba(255,255,255,0.4));
  min-width: 48px;
  text-transform: uppercase;
  letter-spacing: 1px;
}

.agent-selector {
  display: flex;
  background: var(--bg-primary, rgba(255,255,255,0.04));
  border: 1px solid var(--border-color, rgba(255,255,255,0.08));
  border-radius: 0;
  overflow: hidden;
}

.agent-btn {
  padding: 3px 8px;
  background: transparent;
  border: none;
  color: var(--text-muted, rgba(255,255,255,0.4));
  font-size: 10px;
  font-weight: 700;
  font-family: 'Courier New', monospace;
  cursor: pointer;
  transition: all 0.15s;
}

.agent-btn:not(:last-child) {
  border-right: 1px solid var(--border-color, rgba(255,255,255,0.06));
}

.agent-btn:hover {
  background: var(--bg-hover, rgba(255,255,255,0.06));
  color: var(--text-secondary, rgba(255,255,255,0.7));
}

.agent-btn.active {
  background: rgba(99, 102, 241, 0.35);
  color: #c7d2fe;
}

.session-select {
  flex: 1;
  min-width: 0;
  padding: 3px 6px;
  background: var(--bg-primary, rgba(255,255,255,0.04));
  border: 1px solid var(--border-color, rgba(255,255,255,0.08));
  border-radius: 0;
  color: var(--text-primary, rgba(255,255,255,0.8));
  font-size: 10px;
  font-family: 'Courier New', monospace;
  cursor: pointer;
}

.session-select:focus {
  outline: none;
  border-color: rgba(99, 102, 241, 0.4);
}

.session-select option {
  background: #0a0a10;
  color: #ccc;
}

.new-session-btn {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  background: rgba(34, 197, 94, 0.12);
  border: 1px solid rgba(34, 197, 94, 0.25);
  border-radius: 0;
  color: #4ade80;
  font-size: 9px;
  font-weight: 700;
  font-family: 'Courier New', monospace;
  cursor: pointer;
  flex-shrink: 0;
  transition: all 0.15s;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.new-session-btn:hover {
  background: rgba(34, 197, 94, 0.2);
  border-color: rgba(34, 197, 94, 0.4);
  color: #86efac;
}

.spinner-sm {
  width: 12px;
  height: 12px;
  border: 2px solid var(--border-color, rgba(255,255,255,0.1));
  border-top-color: #6366f1;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  flex-shrink: 0;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ── Mic settings row ── */
.mode-toggle-btn {
  padding: 2px 6px;
  font-size: 9px;
  font-weight: 700;
  font-family: 'Courier New', monospace;
  border: 1px solid var(--border-color, rgba(255,255,255,0.08));
  border-radius: 0;
  background: transparent;
  color: var(--text-muted, rgba(255,255,255,0.4));
  cursor: pointer;
  transition: all 0.15s;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  flex-shrink: 0;
}

.mode-toggle-btn:hover {
  background: var(--bg-hover, rgba(255,255,255,0.06));
  color: var(--text-secondary, rgba(255,255,255,0.7));
}

.mode-toggle-btn.gpu {
  background: rgba(16, 185, 129, 0.12);
  border-color: rgba(16, 185, 129, 0.25);
  color: #4ade80;
}

.whisper-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.whisper-dot.offline {
  background: #6b7280;
}

.whisper-dot.loading {
  background: #f59e0b;
  animation: pulse-dot 1.2s ease-in-out infinite;
}

@keyframes pulse-dot {
  0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
  40% { opacity: 1; transform: scale(1); }
}

.whisper-dot.ready {
  background: #10b981;
  box-shadow: 0 0 6px rgba(16, 185, 129, 0.5);
}

.play-audio-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 1px solid var(--border-color, rgba(255,255,255,0.08));
  border-radius: 0;
  background: transparent;
  color: var(--text-muted, rgba(255,255,255,0.4));
  cursor: pointer;
  flex-shrink: 0;
  transition: all 0.15s;
}

.play-audio-btn:hover {
  background: var(--bg-hover, rgba(255,255,255,0.06));
  color: var(--text-primary, rgba(255,255,255,0.8));
}

.play-audio-btn.playing {
  color: #f59e0b;
}

/* Overlay slider */
.overlay-slider {
  flex: 1;
  min-width: 0;
  height: 4px;
  -webkit-appearance: none;
  appearance: none;
  background: var(--border-color, rgba(255,255,255,0.08));
  border-radius: 0;
  outline: none;
  cursor: pointer;
}

.overlay-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 10px;
  height: 10px;
  background: #0ea5e9;
  border: none;
  border-radius: 0;
  cursor: pointer;
}

.overlay-slider::-moz-range-thumb {
  width: 10px;
  height: 10px;
  background: #0ea5e9;
  border: none;
  border-radius: 0;
  cursor: pointer;
}

.overlay-value {
  font-size: 9px;
  font-weight: 700;
  font-family: 'Courier New', monospace;
  color: var(--text-muted, rgba(255,255,255,0.4));
  min-width: 28px;
  text-align: right;
  flex-shrink: 0;
}


</style>
