<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed, onBeforeUnmount, watch, nextTick } from 'vue'
import { register } from 'vue-advanced-chat'
import Badge from 'primevue/badge'
import Button from 'primevue/button'
import { wsManager } from '../composables/useWebSocket'
import type { WebSocketMessage } from '../composables/useWebSocket'
import { useChannelStore } from '../stores/channel'

register()

const channelStore = useChannelStore()

// Measure container height and feed a concrete pixel value to vue-advanced-chat,
// because height: 100% doesn't reliably resolve through flex chains.
const chatContainerRef = ref<HTMLElement | null>(null)
const chatHeight = ref('400px')
let resizeObserver: ResizeObserver | null = null

onMounted(() => {
  if (chatContainerRef.value) {
    resizeObserver = new ResizeObserver(entries => {
      const h = entries[0].contentRect.height
      if (h > 0) chatHeight.value = h + 'px'
    })
    resizeObserver.observe(chatContainerRef.value)
  }
})

onBeforeUnmount(() => {
  resizeObserver?.disconnect()
})

const SESSION_ID = 'default'
const CURRENT_USER_ID = 'user-123'
const AGENT_ID = 'agent'
const BACKEND_URL = 'http://localhost:8080'

// Chat state
const messages = ref<any[]>([])
const messagesLoaded = ref(false)
const isConnected = wsManager.isConnected

// Reactive rooms that update based on WebSocket connection and agent processing state
const rooms = computed(() => [{
  roomId: SESSION_ID,
  roomName: 'AI Agent',
  avatar: null,
  users: [
    { _id: CURRENT_USER_ID, username: 'You' },
    { _id: AGENT_ID, username: 'AI Agent' }
  ],
  unreadCount: 0,
  typingUsers: isAgentProcessing.value ? [AGENT_ID] : []
}])

// Streaming state
let currentStreamingMessageId: string | null = null
let toolCallMessageId: string | null = null
let lastSentMessageId: string | null = null
let streamingBuffer = ''
const isAgentProcessing = ref(false)

const addToolCallBubble = (label: string) => {
  removeToolCallBubble()
  toolCallMessageId = `tool-call-${Date.now()}`
  const timestamp = new Date().toTimeString().split(' ')[0].slice(0, 5)
  messages.value = [...messages.value, {
    _id: toolCallMessageId,
    content: `⚙ ${label}`,
    senderId: AGENT_ID,
    timestamp,
    date: new Date().toLocaleDateString(),
    saved: false,
    distributed: false,
    seen: false,
    files: [],
    toolCall: true
  }]
}

const appendToolCallStatus = (status: string) => {
  if (!toolCallMessageId) return
  const idx = messages.value.findIndex(m => m._id === toolCallMessageId)
  if (idx !== -1) {
    messages.value[idx] = {
      ...messages.value[idx],
      content: messages.value[idx].content + `\n↳ ${status}`
    }
    messages.value = [...messages.value]
  }
}

const removeToolCallBubble = () => {
  if (toolCallMessageId) {
    messages.value = messages.value.filter(m => m._id !== toolCallMessageId)
    toolCallMessageId = null
  }
}

// Generate message ID
const generateMessageId = () => `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`

// Storage for images received during streaming
const streamingImages = ref<any[]>([])

// Handle WebSocket messages
const handleMessage = (data: WebSocketMessage) => {
  console.log('[ChatPanel] Received message:', data)

  if (data.type === 'conversation_history') {
    messages.value = (data.messages as any[]).map((m: any) => {
      const ts = new Date(m.timestamp / 1000) // microseconds → ms
      const files = (m.files ?? []).map((b: any) => ({
        name: `image_${b.id}.png`,
        size: 0,
        type: b.mimeType.split('/')[1] ?? 'png',
        url: `data:${b.mimeType};base64,${b.data}`,
        preview: `data:${b.mimeType};base64,${b.data}`,
      }))
      return {
        _id: m.id,
        content: m.content,
        senderId: m.role === 'user' ? CURRENT_USER_ID : AGENT_ID,
        timestamp: ts.toTimeString().split(' ')[0].slice(0, 5),
        date: ts.toLocaleDateString(),
        saved: true,
        distributed: true,
        seen: true,
        files,
      }
    })
    messagesLoaded.value = true
    return
  }

  if (data.type === 'agent_tool_call') {
    addToolCallBubble(data.label ?? data.toolName ?? 'Tool call...')
    return
  }

  if (data.type === 'subagent_tool_call') {
    appendToolCallStatus(data.toolName ?? data.label ?? 'tool')
    return
  }

  if (data.type === 'subagent_chunk') {
    // Subagent final text — not shown separately; the main agent will incorporate it in its response
    return
  }

  if (data.type === 'image') {
    // Handle image message - attach to current streaming message or create standalone
    console.log('[ChatPanel] Processing image message')
    const imageFile = {
      name: `chart_${Date.now()}.png`,
      size: 0,
      type: 'png',
      url: `data:${data.mimeType};base64,${data.data}`,
      preview: `data:${data.mimeType};base64,${data.data}`
    }

    if (currentStreamingMessageId) {
      // Attach to current streaming message
      streamingImages.value.push(imageFile)
      const msgIndex = messages.value.findIndex(m => m._id === currentStreamingMessageId)
      if (msgIndex !== -1) {
        messages.value[msgIndex] = {
          ...messages.value[msgIndex],
          files: [...streamingImages.value]
        }
        messages.value = [...messages.value]
      }
    } else {
      // No active streaming message - create a standalone image message
      const timestamp = new Date().toTimeString().split(' ')[0].slice(0, 5)
      messages.value = [...messages.value, {
        _id: generateMessageId(),
        content: data.caption || '',
        senderId: AGENT_ID,
        timestamp: timestamp,
        date: new Date().toLocaleDateString(),
        saved: true,
        distributed: true,
        seen: true,
        files: [imageFile]
      }]
    }
  } else if (data.type === 'agent_chunk') {
    console.log('[ChatPanel] Processing agent_chunk, content:', data.content, 'done:', data.done)
    const timestamp = new Date().toTimeString().split(' ')[0].slice(0, 5)

    if (!currentStreamingMessageId) {
      console.log('[ChatPanel] Starting new streaming message')
      // Remove any ephemeral tool-call bubble before starting the real response
      removeToolCallBubble()
      // Set up streaming state and mark user message as seen
      isAgentProcessing.value = true
      currentStreamingMessageId = generateMessageId()
      streamingBuffer = data.content
      streamingImages.value = []

      // Mark the last sent user message as seen (double-checkmark)
      if (lastSentMessageId) {
        const idx = messages.value.findIndex(m => m._id === lastSentMessageId)
        if (idx !== -1) {
          messages.value[idx] = { ...messages.value[idx], seen: true }
          messages.value = [...messages.value]
        }
        lastSentMessageId = null
      }

      // Only add the agent bubble once there is actual content to show
      if (data.content) {
        messages.value = [...messages.value, {
          _id: currentStreamingMessageId,
          content: streamingBuffer,
          senderId: AGENT_ID,
          timestamp: timestamp,
          date: new Date().toLocaleDateString(),
          saved: false,
          distributed: false,
          seen: false,
          files: []
        }]
      }
    } else {
      // Update (or lazily create) the streaming message
      streamingBuffer += data.content
      const msgIndex = messages.value.findIndex(m => m._id === currentStreamingMessageId)

      if (msgIndex !== -1) {
        const updatedMessage: any = {
          ...messages.value[msgIndex],
          content: streamingBuffer,
          files: [...streamingImages.value] // Include accumulated images
        }

        // Add plot images if present in metadata
        if (data.metadata && data.metadata.plot_urls && Array.isArray(data.metadata.plot_urls)) {
          const plotFiles = data.metadata.plot_urls.map((url: string, idx: number) => ({
            name: `plot_${idx + 1}.png`,
            size: 0,
            type: 'png',
            url: `${BACKEND_URL}${url}`,
            preview: `${BACKEND_URL}${url}`
          }))
          updatedMessage.files = [...updatedMessage.files, ...plotFiles]
        }

        messages.value[msgIndex] = updatedMessage
        messages.value = [...messages.value]
      } else if (streamingBuffer) {
        // First chunk with content after an empty ack — create the bubble now
        const timestamp2 = new Date().toTimeString().split(' ')[0].slice(0, 5)
        messages.value = [...messages.value, {
          _id: currentStreamingMessageId!,
          content: streamingBuffer,
          senderId: AGENT_ID,
          timestamp: timestamp2,
          date: new Date().toLocaleDateString(),
          saved: false,
          distributed: false,
          seen: false,
          files: [...streamingImages.value]
        }]
      }
    }

    if (data.done) {
      // Mark message as complete
      const msgIndex = messages.value.findIndex(m => m._id === currentStreamingMessageId)
      if (msgIndex !== -1) {
        const finalMessage: any = {
          ...messages.value[msgIndex],
          saved: true,
          distributed: true,
          seen: true,
          files: [...streamingImages.value] // Include all accumulated images
        }

        // Ensure plot images are included in final message
        if (data.metadata && data.metadata.plot_urls && Array.isArray(data.metadata.plot_urls)) {
          const plotFiles = data.metadata.plot_urls.map((url: string, idx: number) => ({
            name: `plot_${idx + 1}.png`,
            size: 0,
            type: 'png',
            url: `${BACKEND_URL}${url}`,
            preview: `${BACKEND_URL}${url}`
          }))
          finalMessage.files = [...finalMessage.files, ...plotFiles]
        }

        messages.value[msgIndex] = finalMessage
        messages.value = [...messages.value]
      }

      currentStreamingMessageId = null
      streamingBuffer = ''
      streamingImages.value = []
      isAgentProcessing.value = false
      removeToolCallBubble()
    }
  }
}

// Stop agent processing
const stopAgent = () => {
  wsManager.send({ type: 'agent_stop', session_id: SESSION_ID })
  isAgentProcessing.value = false
  removeToolCallBubble()
  lastSentMessageId = null
}

// Send message handler
const sendMessage = async (event: any) => {
  // Extract data from CustomEvent.detail[0]
  const data = event.detail?.[0] || event

  const content = data.content || ''
  const files = data.files
  const roomId = data.roomId

  const timestamp = new Date().toTimeString().split(' ')[0].slice(0, 5)
  const messageId = generateMessageId()

  // Process files
  let attachments: string[] = []
  let messageFiles: any[] = []

  if (files && files.length > 0) {
    // Upload files to backend
    for (const file of files) {
      try {
        const formData = new FormData()
        // Convert blob URL to actual file if needed
        const response = await fetch(file.url)
        const blob = await response.blob()
        const actualFile = new File([blob], file.name, { type: file.type })
        formData.append('file', actualFile)

        const uploadResponse = await fetch(`${BACKEND_URL}/api/upload`, {
          method: 'POST',
          body: formData
        })

        if (!uploadResponse.ok) {
          throw new Error('Upload failed')
        }

        const uploadData = await uploadResponse.json()
        const fileUrl = `${BACKEND_URL}${uploadData.url}`
        attachments.push(fileUrl)

        messageFiles.push({
          name: file.name,
          size: uploadData.size,
          type: file.extension || file.type,
          url: fileUrl,
          preview: file.url // Keep the blob URL for preview
        })
      } catch (error) {
        console.error('File upload error:', error)
      }
    }
  }

  // Add user message to UI
  const userMessage = {
    _id: messageId,
    content: content,
    senderId: CURRENT_USER_ID,
    timestamp: timestamp,
    date: new Date().toLocaleDateString(),
    saved: true,
    distributed: false,
    seen: false,
    files: messageFiles
  }

  messages.value = [...messages.value, userMessage]

  // Send to backend via WebSocket
  const wsMessage = {
    type: 'agent_user_message',
    session_id: roomId || SESSION_ID,
    content: content,
    attachments: attachments
  }
  wsManager.send(wsMessage)

  // Track this message so the agent_chunk handler can mark it seen
  lastSentMessageId = messageId
  // Show typing indicator immediately (before first chunk arrives)
  isAgentProcessing.value = true

  // Add thinking bubble in a macrotask so it runs in the same execution context
  // as the WebSocket handler (where tool bubbles work). nextTick / sync both fail
  // because vue-advanced-chat processes the send-message event asynchronously.
  setTimeout(() => addToolCallBubble('Thinking...'), 0)

  // Mark as distributed (single checkmark) after confirming WS send
  setTimeout(() => {
    const msgIndex = messages.value.findIndex(m => m._id === messageId)
    if (msgIndex !== -1) {
      messages.value[msgIndex] = {
        ...messages.value[msgIndex],
        distributed: true,
      }
      messages.value = [...messages.value]
    }
  }, 100)
}

// Fetch messages handler (for pagination, not needed for simple chat)
const fetchMessages = ({ room, options }: any) => {
  messagesLoaded.value = true
}

// Open file handler
const openFile = ({ file }: any) => {
  window.open(file.url, '_blank')
}

// Styles to match TradingView dark theme
const chatStyles = {
  general: {
    color: '#dbdbdb',
    colorButtonClear: '#dbdbdb',
    colorButton: '#0f0f0f',
    backgroundColorButton: '#089981',
    backgroundInput: '#0f0f0f',
    colorPlaceholder: '#8a8a8a',
    colorCaret: '#dbdbdb',
    colorSpinner: '#089981',
    borderStyle: '1px solid #2e2e2e',
    backgroundScrollIcon: '#1c1c1c'
  },
  container: {
    border: 'none',
    borderRadius: '0',
    boxShadow: 'none'
  },
  header: {
    background: '#1c1c1c',
    colorRoomName: '#dbdbdb',
    colorRoomInfo: '#8a8a8a',
    position: 'absolute',
    width: '100%'
  },
  footer: {
    background: '#1c1c1c',
    borderStyleInput: '1px solid #2e2e2e',
    borderInputSelected: '#089981',
    backgroundReply: '#1c1c1c',
    backgroundTagActive: '#2e2e2e',
    backgroundTag: '#141414'
  },
  content: {
    background: '#0f0f0f'
  },
  sidemenu: {
    background: '#0f0f0f',
    backgroundHover: '#141414',
    backgroundActive: '#1c1c1c',
    colorActive: '#dbdbdb',
    borderColorSearch: '#2e2e2e'
  },
  dropdown: {
    background: '#1c1c1c',
    backgroundHover: '#2e2e2e'
  },
  message: {
    background: '#141414',
    backgroundMe: '#089981',
    color: '#dbdbdb',
    colorStarted: '#8a8a8a',
    backgroundDeleted: '#0f0f0f',
    backgroundSelected: '#2e2e2e',
    colorDeleted: '#8a8a8a',
    colorUsername: '#8a8a8a',
    colorTimestamp: '#8a8a8a',
    backgroundDate: 'rgba(0, 0, 0, 0.4)',
    colorDate: '#8a8a8a',
    backgroundSystem: 'rgba(0, 0, 0, 0.4)',
    colorSystem: '#8a8a8a',
    backgroundMedia: 'rgba(0, 0, 0, 0.2)',
    backgroundReply: 'rgba(0, 0, 0, 0.2)',
    colorReplyUsername: '#dbdbdb',
    colorReply: '#b8b8b8',
    colorTag: '#089981',
    backgroundImage: '#1c1c1c',
    colorNewMessages: '#089981',
    backgroundScrollCounter: '#089981',
    colorScrollCounter: '#0f0f0f',
    backgroundReaction: 'none',
    borderStyleReaction: 'none',
    backgroundReactionHover: '#2e2e2e',
    borderStyleReactionHover: 'none',
    colorReactionCounter: '#dbdbdb',
    backgroundReactionMe: '#089981',
    borderStyleReactionMe: 'none',
    backgroundReactionHoverMe: '#089981',
    borderStyleReactionHoverMe: 'none',
    colorReactionCounterMe: '#0f0f0f',
    backgroundAudioRecord: '#f23645',
    backgroundAudioLine: 'rgba(255, 255, 255, 0.15)',
    backgroundAudioProgress: '#089981',
    backgroundAudioProgressSelector: '#089981',
    colorFileExtension: '#8a8a8a'
  },
  markdown: {
    background: 'rgba(28, 28, 28, 0.9)',
    border: 'rgba(46, 46, 46, 0.9)',
    color: '#089981',
    colorMulti: '#dbdbdb'
  },
  room: {
    colorUsername: '#dbdbdb',
    colorMessage: '#8a8a8a',
    colorTimestamp: '#8a8a8a',
    colorStateOnline: '#089981',
    colorStateOffline: '#8a8a8a',
    backgroundCounterBadge: '#089981',
    colorCounterBadge: '#0f0f0f'
  },
  emoji: {
    background: '#1c1c1c'
  },
  icons: {
    search: '#8a8a8a',
    add: '#dbdbdb',
    toggle: '#dbdbdb',
    menu: '#dbdbdb',
    close: '#8a8a8a',
    closeImage: '#dbdbdb',
    file: '#089981',
    paperclip: '#8a8a8a',
    closeOutline: '#dbdbdb',
    closePreview: '#dbdbdb',
    send: '#089981',
    sendDisabled: '#8a8a8a',
    emoji: '#8a8a8a',
    emojiReaction: '#8a8a8a',
    document: '#089981',
    pencil: '#8a8a8a',
    checkmark: '#8a8a8a',
    checkmarkSeen: '#089981',
    eye: '#dbdbdb',
    dropdownMessage: '#dbdbdb',
    dropdownMessageBackground: 'rgba(0, 0, 0, 0.4)',
    dropdownRoom: '#dbdbdb',
    dropdownScroll: '#1c1c1c',
    microphone: '#8a8a8a',
    audioPlay: '#089981',
    audioPause: '#089981',
    audioCancel: '#f23645',
    audioConfirm: '#089981'
  }
}

onMounted(() => {
  wsManager.addHandler(handleMessage)

  setTimeout(() => {
    // Inject styles into shadow DOM to widen message bubbles (fallback if already ready at mount)
    injectShadowStyles()

    const chatInput = document.querySelector('.vac-textarea') as HTMLTextAreaElement
    if (chatInput) {
      chatInput.focus()
    }
  }, 300)
})

const injectShadowStyles = () => {
  const chatEl = document.querySelector('vue-advanced-chat')
  if (chatEl?.shadowRoot) {
    // Remove any previously injected style to avoid duplicates
    chatEl.shadowRoot.querySelector('#vac-width-override')?.remove()
    const style = document.createElement('style')
    style.id = 'vac-width-override'
    style.textContent = `
      .vac-message-wrapper .vac-message-box { max-width: 80%; }
      .vac-message-wrapper .vac-offset-current { margin-left: 20%; }
    `
    chatEl.shadowRoot.appendChild(style)
  }
}

watch(() => channelStore.isReady, async (ready) => {
  if (!ready) return
  await nextTick()
  setTimeout(() => {
    injectShadowStyles()
    const chatInput = document.querySelector('.vac-textarea') as HTMLTextAreaElement
    chatInput?.focus()
  }, 100)
})

onUnmounted(() => {
  wsManager.removeHandler(handleMessage)
})
</script>

<template>
  <div class="chat-container" ref="chatContainerRef">
<!--
    <div class="chat-header-custom">
      <span class="chat-title">AI Agent Chat</span>
      <Badge
        :value="isConnected ? 'Connected' : 'Disconnected'"
        :severity="isConnected ? 'success' : 'danger'"
      />
    </div>
-->

    <!-- Workspace loading overlay -->
    <div v-if="!channelStore.isReady" class="workspace-loading">
      <i class="pi pi-spin pi-spinner workspace-loading-spinner" />
      <span class="workspace-loading-message">{{ channelStore.statusMessage || 'Connecting...' }}</span>
    </div>

    <vue-advanced-chat
      v-else
      :height="chatHeight"
      :current-user-id="CURRENT_USER_ID"
      :rooms="JSON.stringify(rooms)"
      :messages="JSON.stringify(messages)"
      :messages-loaded="messagesLoaded"
      :room-id="SESSION_ID"
      :styles="JSON.stringify(chatStyles)"
      :single-room="true"
      :rooms-list-opened="false"
      :show-add-room="false"
      :show-audio="false"
      :show-files="false"
      :show-emojis="true"
      :show-reaction-emojis="false"
      :message-actions="JSON.stringify([])"
      :accepted-files="'image/*,video/*,application/pdf'"
      :message-images="true"
      @send-message="sendMessage"
      @fetch-messages="fetchMessages"
      @open-file="openFile"
    />

    <!-- Stop button overlay -->
    <div v-if="isAgentProcessing" class="stop-button-container">
      <Button
        icon="pi pi-stop-circle"
        label="Stop"
        severity="danger"
        @click="stopAgent"
        class="stop-button"
      />
    </div>
  </div>
</template>

<style scoped>
.chat-container {
  height: 100% !important;
  display: flex;
  flex-direction: column;
  background: var(--p-surface-0) !important;
  overflow: hidden;
  position: relative;
}

.workspace-loading {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  background: #0f0f0f;
  color: #888;
}

.workspace-loading-spinner {
  font-size: 2rem;
  color: #089981;
}

.workspace-loading-message {
  font-size: 0.875rem;
}

.chat-container :deep(.vac-container) {
  height: 100% !important;
}

.chat-container :deep(.vac-text-message) {
  white-space: pre-wrap;
}

/* Override the default 50% desktop width on the inner message box */
.chat-container :deep(.vac-message-wrapper .vac-message-box) {
  flex: 0 0 80% !important;
  max-width: 80% !important;
}


.chat-header-custom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 1.5rem;
  background: var(--p-surface-50);
  border-bottom: 1px solid var(--p-surface-200);
  flex-shrink: 0;
}

.chat-title {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--p-surface-900);
}

.stop-button-container {
  position: absolute;
  bottom: 80px;
  right: 20px;
  z-index: 1000;
}

.stop-button {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.8;
  }
}
</style>
