import { defineStore } from 'pinia'
import { reactive } from 'vue'
import { API } from '@/Api'
import type {
  Message,
  Role,
  EventLogEntry,
  InitialDraft,
  MessageAttachment,
} from '@/types/messages'

export interface ConversationState {
  id: string
  messages: Message[]
  agentIds: string[]
  modeAgents: boolean
  selectedLLM: string | null
  title?: string
  lastUpdated: string
  isStreaming: boolean
  streamingReply: string
  liveEvents: Array<{ kind: string; data: any }>
  detailedEventLog: EventLogEntry[]
  hasPendingLiveEvent: boolean
  isInitializing: boolean
  draftMode?: boolean
  initialDraft?: InitialDraft | null
  conversation_attachments?: MessageAttachment[]
  has_attachments?: boolean
}

interface ConversationsById {
  [conversationId: string]: ConversationState
}

// Defined type for the store state
export interface ConversationStoreState {
  conversations: ConversationsById
  activeConversationId: string | null
  loading: boolean
  initialMessage: Message | null
  agentIds: string[]
}

export const useConversationStore = defineStore('conversation', {
  state: (): ConversationStoreState => ({
    conversations: reactive<ConversationsById>({}),
    activeConversationId: null,
    loading: false,
    initialMessage: null,
    agentIds: [],
  }),

  getters: {
    activeConversation: (state): ConversationState | null => {
      if (!state.activeConversationId) return null
      return state.conversations[state.activeConversationId] || null
    },

    activeMessages: (state): Message[] => {
      const active = state.activeConversationId
        ? state.conversations[state.activeConversationId]
        : null
      if (!active) return []

      if (!active.isStreaming || !active.streamingReply) {
        return active.messages
      }

      return [
        ...active.messages,
        {
          role: 'assistant' as Role,
          content: active.streamingReply,
          hasEventLog: false,
        } satisfies Message,
      ]
    },

    isConversationEmpty: (state): boolean => {
      const active = state.activeConversationId
        ? state.conversations[state.activeConversationId]
        : null
      return !active || active.messages.length === 0
    },

    hasSelectedAgents: (state): boolean => {
      const active = state.activeConversationId
        ? state.conversations[state.activeConversationId]
        : null
      return active ? active.agentIds.length > 0 : false
    },

    showEmptyState: (state): boolean => {
      const active = state.activeConversationId
        ? state.conversations[state.activeConversationId]
        : null
      return (
        !active ||
        (!active.isInitializing && active.agentIds.length === 0 && active.messages.length === 0)
      )
    },

    showAgentPromptState: (state): boolean => {
      const active = state.activeConversationId
        ? state.conversations[state.activeConversationId]
        : null
      return (
        !active ||
        (!active.isInitializing && active.agentIds.length > 0 && active.messages.length === 0)
      )
    },

    isActiveConversationInitializing: (state): boolean => {
      const conversation = state.activeConversationId
        ? state.conversations[state.activeConversationId]
        : null
      return conversation ? conversation.isInitializing : false
    },

    allConversationIds: (state): string[] => {
      return Object.keys(state.conversations)
    },

    conversationsWithStreaming: (state): string[] => {
      return Object.keys(state.conversations).filter((id) => state.conversations[id].isStreaming)
    },

    conversationTitleById: (state) => {
      return (conversationId: string) => {
        const conversation = state.conversations[conversationId]
        return conversation ? conversation.title || '' : ''
      }
    },
  },
  actions: {
    // Initialize a new conversation
    initializeConversation(
      conversationId: string,
      agentIds: string[] = [],
      modeAgents: boolean,
      selectedLLM: string | null,
      options: {
        draftMode?: boolean
        initialDraft?: InitialDraft | null
        skipInitialLoad?: boolean
        initialMessage?: Message
      } = {}
    ) {
      if (this.conversations[conversationId]) {
        return
      }

      const now = new Date().toISOString()
      this.conversations[conversationId] = {
        id: conversationId,
        messages: [],
        agentIds: [...agentIds],
        modeAgents: modeAgents,
        selectedLLM: selectedLLM,
        lastUpdated: now,
        isStreaming: false,
        streamingReply: '',
        liveEvents: [],
        detailedEventLog: [],
        hasPendingLiveEvent: false,
        isInitializing: true,
        draftMode: options.draftMode || false,
        initialDraft: options.initialDraft || null,
      }

      if (options.initialMessage) {
        this.initialMessage = options.initialMessage
      }
    },

    // Set the active conversation
    setActiveConversation(conversationId: string | null) {
      this.activeConversationId = conversationId
    },

    // Load conversation data from API
    async loadConversation(
      conversationId: string,
      options: {
        draftMode?: boolean
        initialDraft?: InitialDraft | null
        skipInitialLoad?: boolean
        agentIds?: string[]
        modeAgents?: boolean
        selectedLLM?: string | null
      } = {}
    ) {
      let conversation = this.conversations[conversationId]
      if (!conversation) {
        this.initializeConversation(
          conversationId,
          options.agentIds || [],
          options.modeAgents ?? true,
          options.selectedLLM ?? null,
          options
        )
        conversation = this.conversations[conversationId]
      }

      conversation.isInitializing = true

      try {
        if (options.skipInitialLoad) {
          conversation.isInitializing = false
          return
        }

        if (options.draftMode && options.initialDraft?.id === conversationId) {
          conversation.messages = options.initialDraft.messages
          conversation.agentIds = options.initialDraft.agentIds
          conversation.isInitializing = false
          return
        }

        if (options.draftMode) {
          const { data } = await API.getDraftConversation(options.agentIds?.[0] || '')
          conversation.messages = data?.messages || []
          conversation.agentIds = data?.agentIds || [...(options.agentIds || [])]
          conversation.modeAgents = true
          conversation.selectedLLM = null
        } else {
          const { data } = await API.getConversation(conversationId)
          conversation.messages = data.messages || []
          conversation.agentIds =
            data.agentIds?.length > 0 ? data.agentIds : [...(options.agentIds || [])]
          conversation.modeAgents = data.modeAgents ?? true
          conversation.selectedLLM = data.selectedLLM
          conversation.conversation_attachments = data.conversation_attachments || []
          conversation.has_attachments = !!data.has_attachments
        }
      } catch (err: any) {
        if (!(options.draftMode && err.response?.status === 404)) {
          console.error('Error loading conversation', err)
        }
      } finally {
        conversation.isInitializing = false
        conversation.lastUpdated = new Date().toISOString()
      }
    },

    // Add a message to the conversation
    addMessage(conversationId: string, message: Message) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.messages.push(message)
      conversation.lastUpdated = new Date().toISOString()
    },

    // Update messages array
    setMessages(conversationId: string, messages: Message[]) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.messages = [...messages]
      conversation.lastUpdated = new Date().toISOString()
    },

    // Update a specific message
    updateMessage(conversationId: string, messageId: string, updates: Partial<Message>) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      const messageIndex = conversation.messages.findIndex((m) => m.id === messageId)
      if (messageIndex !== -1) {
        conversation.messages[messageIndex] = { ...conversation.messages[messageIndex], ...updates }
        conversation.lastUpdated = new Date().toISOString()
      }
    },

    // Truncate messages from a specific point
    truncateMessagesFrom(conversationId: string, messageId: string) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      const messageIndex = conversation.messages.findIndex((m) => m.id === messageId)
      if (messageIndex !== -1) {
        conversation.messages = conversation.messages.slice(0, messageIndex + 1)
        conversation.lastUpdated = new Date().toISOString()
      }
    },

    // Update agent IDs for a conversation
    updateAgentIds(conversationId: string, agentIds: string[]) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.agentIds = [...agentIds]
      conversation.lastUpdated = new Date().toISOString()
    },

    updateSelectedLLM(conversationId: string, selectedLLM: string | null) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return
      conversation.selectedLLM = selectedLLM
      conversation.lastUpdated = new Date().toISOString()
    },
    updateModeAgents(conversationId: string, modeAgents: boolean) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return
      conversation.modeAgents = modeAgents
      conversation.lastUpdated = new Date().toISOString()
    },
    updateHasAttachments(conversationId: string, hasAttachments: boolean) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return
      conversation.has_attachments = hasAttachments
      conversation.lastUpdated = new Date().toISOString()
    },

    updateExtractionMode(conversationId: string, mode: string | null, quotaExceeded?: boolean) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return
      // Update the latest message's extraction mode and quota exceeded flag
      const messages = conversation.messages || []
      if (messages.length > 0) {
        const latestMessage = messages[messages.length - 1]
        if (latestMessage) {
          latestMessage.extractionMode = mode
          if (quotaExceeded !== undefined) {
            latestMessage.quotaExceeded = quotaExceeded
          }
        }
      }
      conversation.lastUpdated = new Date().toISOString()
    },

    getLatestExtractionMode(conversationId: string): string | null {
      const conversation = this.conversations[conversationId]
      if (!conversation) return null
      const messages = conversation.messages || []
      // Find extraction mode from latest message with attachments
      for (let i = messages.length - 1; i >= 0; i--) {
        if (messages[i].extractionMode) {
          return messages[i].extractionMode || null
        }
      }
      return null
    },

    getLatestQuotaExceeded(conversationId: string): boolean {
      const conversation = this.conversations[conversationId]
      if (!conversation) return false
      const messages = conversation.messages || []
      // Find quota exceeded flag from latest message with extraction mode
      for (let i = messages.length - 1; i >= 0; i--) {
        if (messages[i].extractionMode && messages[i].quotaExceeded !== undefined) {
          return messages[i].quotaExceeded || false
        }
      }
      return false
    },

    setConversationAttachments(conversationId: string, attachments: MessageAttachment[]) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return
      conversation.conversation_attachments = attachments
    },

    // Update conversation title
    updateTitle(conversationId: string, title: string) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.title = title
      conversation.lastUpdated = new Date().toISOString()
    },

    // Streaming state management
    setStreamingState(conversationId: string, isStreaming: boolean) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.isStreaming = isStreaming
      if (!isStreaming) {
        conversation.streamingReply = ''
        conversation.liveEvents = []
        conversation.detailedEventLog = []
        conversation.hasPendingLiveEvent = false
      }
    },

    setStreamingReply(conversationId: string, reply: string) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.streamingReply = reply
    },

    setLiveEvents(conversationId: string, events: Array<{ kind: string; data: any }> | undefined) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.liveEvents = Array.isArray(events) ? [...events] : []
    },

    setDetailedEventLog(conversationId: string, eventLog: EventLogEntry[] | undefined) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      if (!Array.isArray(eventLog) && eventLog !== undefined) {
        console.warn(
          'setDetailedEventLog received non-array value:',
          eventLog,
          'for conversation:',
          conversationId
        )
      }

      conversation.detailedEventLog = Array.isArray(eventLog) ? [...eventLog] : []
    },

    setPendingLiveEvent(conversationId: string, hasPending: boolean) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.hasPendingLiveEvent = hasPending
    },

    // Clear conversation state
    clearConversation(conversationId: string) {
      const conversation = this.conversations[conversationId]
      if (!conversation) return

      conversation.messages = []
      conversation.isStreaming = false
      conversation.streamingReply = ''
      conversation.liveEvents = []
      conversation.detailedEventLog = []
      conversation.hasPendingLiveEvent = false
      conversation.isInitializing = false
      conversation.lastUpdated = new Date().toISOString()
    },

    clearConversationSelection() {
      this.activeConversationId = null
      this.agentIds = []
    },

    // Remove conversation from store
    removeConversation(conversationId: string) {
      delete this.conversations[conversationId]
      if (this.activeConversationId === conversationId) {
        this.activeConversationId = null
      }
    },

    // Get conversation by ID
    getConversation(conversationId: string): ConversationState | null {
      return this.conversations[conversationId] || null
    },

    // Check if conversation exists
    hasConversation(conversationId: string): boolean {
      return conversationId in this.conversations
    },

    // Get all conversations
    getAllConversations(): ConversationState[] {
      return Object.values(this.conversations)
    },

    // Cleanup old conversations (optional - for memory management)
    cleanupOldConversations(keepIds: string[] = []) {
      const now = Date.now()
      const oneHour = 60 * 60 * 1000 // 1 hour

      Object.keys(this.conversations).forEach((id) => {
        if (keepIds.includes(id)) return

        const conversation = this.conversations[id]
        const lastUpdated = new Date(conversation.lastUpdated).getTime()

        // Remove conversations older than 1 hour that are not streaming
        if (now - lastUpdated > oneHour && !conversation.isStreaming) {
          delete this.conversations[id]
        }
      })
    },

    // Updated setInitialMessage to use root state
    setInitialMessage(message?: Message) {
      this.initialMessage = message || null
    },

    setAgentIds(agentIds: string[]) {
      this.agentIds = [...agentIds]
    },
  },
})
