<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
import type {
  ParsedConversation,
  ParsedUserMessage,
  ParsedAssistantMessage,
  ParsedSystemMessage,
  ConversationMessage
} from '@/types/transcript-debug'
import UserMessageBubble from './UserMessageBubble.vue'
import AssistantMessageBubble from './AssistantMessageBubble.vue'
import ProgressEvent from './ProgressEvent.vue'
import SystemMessage from './SystemMessage.vue'
import UserInput from './UserInput.vue'

const props = defineProps<{
  conversation: ParsedConversation
  processing?: boolean
}>()

const emit = defineEmits<{
  send: [message: string]
}>()

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)
}

// ── 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>())

// 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') {
      currentUserUuid = msg.uuid
    } else if (currentUserUuid) {
      map.set(msg.uuid, currentUserUuid)
    }
  }
  return map
})

// Count of non-user messages per section
const sectionCounts = computed(() => {
  const counts = new Map<string, number>()
  let currentUserUuid: string | null = null
  for (const msg of props.conversation.messages) {
    if (msg.kind === 'user') {
      currentUserUuid = msg.uuid
      if (!counts.has(currentUserUuid)) counts.set(currentUserUuid, 0)
    } else if (currentUserUuid) {
      counts.set(currentUserUuid, (counts.get(currentUserUuid) || 0) + 1)
    }
  }
  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 {
  if (msg.kind === 'user') return false
  const owner = sectionMap.value.get(msg.uuid)
  return !!owner && collapsedSections.value.has(owner)
}

// 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">
    <div class="chat-header">
      <div class="chat-title-row">
        <span class="session-id" :title="conversation.sessionId">{{ conversation.sessionId }}</span>
        <button class="copy-id-btn" :class="{ copied: idCopied }" @click="copySessionId" title="Copy session ID">
          <svg v-if="!idCopied" width="12" height="12" 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="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <polyline points="20 6 9 17 4 12"/>
          </svg>
        </button>
        <div class="header-spacer"></div>
        <button :class="['select-mode-btn', { active: selectMode }]" @click="toggleSelectMode" title="Toggle select mode">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <polyline v-if="selectMode" points="20 6 9 17 4 12"/>
            <template v-else>
              <rect x="3" y="3" width="7" height="7" rx="1"/>
              <rect x="14" y="3" width="7" height="7" rx="1"/>
              <rect x="3" y="14" width="7" height="7" rx="1"/>
              <rect x="14" y="14" width="7" height="7" rx="1"/>
            </template>
          </svg>
          <span class="select-mode-label">{{ selectMode ? 'Done' : 'Select' }}</span>
        </button>
      </div>
      <div class="chat-meta">
        <span v-if="conversation.model" class="meta-badge model">{{ conversation.model }}</span>
        <span v-if="conversation.version" class="meta-badge version">v{{ conversation.version }}</span>
        <span v-if="conversation.metadata.cwd" class="meta-cwd" :title="conversation.metadata.cwd">
          {{ conversation.metadata.cwd }}
        </span>
        <span class="meta-duration">
          {{ formatDuration(conversation.metadata.startTime, conversation.metadata.endTime) }}
        </span>
        <span class="meta-count">{{ conversation.messages.length }} messages</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"
              @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"
            />
            <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>

    <UserInput
      :processing="props.processing"
      @send="emit('send', $event)"
    />
  </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 {
  padding: 0.5rem 0.75rem;
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--border-color);
  flex-shrink: 0;
}

.chat-title-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.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;
}

.session-id {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  font-family: 'SF Mono', 'Fira Code', monospace;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 320px;
  letter-spacing: 0.3px;
}

.copy-id-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: none;
  background: transparent;
  border-radius: 4px;
  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;
}

.chat-meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.25rem;
  flex-wrap: wrap;
}

.meta-badge {
  font-size: 10px;
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  font-family: 'SF Mono', 'Fira Code', monospace;
}

.meta-badge.model {
  background: rgba(99, 102, 241, 0.1);
  color: var(--accent, #6366f1);
}

.meta-badge.version {
  background: var(--bg-hover);
  color: var(--text-muted);
}

.meta-cwd {
  font-size: 10px;
  color: var(--text-muted);
  font-family: 'SF Mono', 'Fira Code', monospace;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 250px;
}

.meta-duration {
  font-size: 10px;
  color: var(--text-muted);
  font-family: 'SF Mono', 'Fira Code', monospace;
}

.meta-count {
  font-size: 10px;
  color: var(--text-muted);
  margin-left: auto;
}

.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; }
}
</style>
