import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { BasketItem } from '@/types/basket'
import type { GraphData } from '@/types/graph'
import type { QAReference, QAMessage } from '@/types/qa'
import { getBasketItemKey } from '@/utils/basketKeys'

export interface QASession {
  id: string
  title: string
  createdAt: number
  updatedAt: number
  messages: QAMessage[]
  contextItems: BasketItem[]
}

const STORAGE_KEY = 'qa-sessions'

// 深拷贝：使用 JSON 序列化/反序列化，兼容 Vue reactive proxy 和 localStorage 恢复的数据
// structuredClone 无法克隆 Vue Proxy 对象，会抛 DataCloneError
function clone<T>(value: T): T {
  return JSON.parse(JSON.stringify(value))
}

function createSessionRecord(title?: string): QASession {
  const now = Date.now()
  return {
    // 兼容非安全上下文（HTTP 局域网访问），fallback 到 Math.random
    id: `qa-${typeof crypto?.randomUUID === 'function' ? crypto.randomUUID() : Math.random().toString(36).slice(2) + Date.now().toString(36)}`,
    title: title || '新问答会话',
    createdAt: now,
    updatedAt: now,
    messages: [],
    contextItems: [],
  }
}

function getReferenceKey(reference: Pick<QAReference, 'doc_id' | 'source_group' | 'profile_id'>): string {
  if (reference.source_group === 'guide') {
    return `guide:${reference.profile_id || reference.doc_id}`
  }
  return `doc:${reference.doc_id}`
}

function loadSessions(): QASession[] {
  const raw = localStorage.getItem(STORAGE_KEY)
  if (!raw) return [createSessionRecord()]

  try {
    const parsed = JSON.parse(raw)
    if (!Array.isArray(parsed) || !parsed.length) {
      return [createSessionRecord()]
    }
    return parsed
  } catch {
    return [createSessionRecord()]
  }
}

export const useQAStore = defineStore('qa', () => {
  const sessions = ref<QASession[]>(loadSessions())
  const messages = ref<QAMessage[]>([])
  const references = ref<QAReference[]>([])
  const graphData = ref<GraphData | null>(null)
  const contextItems = ref<BasketItem[]>([])
  const isStreaming = ref(false)
  const sessionId = ref<string | undefined>(sessions.value[0]?.id)
  const currentAssistantContent = ref('')

  function persistSessions() {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(sessions.value))
  }

  function hydrateSession(id: string | undefined) {
    const session = sessions.value.find((item) => item.id === id)
    messages.value = clone(session?.messages || [])
    contextItems.value = clone(session?.contextItems || [])

    const lastAssistant = [...messages.value].reverse().find((item) => item.role === 'assistant')
    references.value = lastAssistant?.references ? [...lastAssistant.references] : []
    graphData.value = lastAssistant?.graphData ? { ...lastAssistant.graphData } : null
  }

  function syncCurrentSession() {
    const current = sessions.value.find((item) => item.id === sessionId.value)
    if (!current) return
    current.messages = clone(messages.value)
    current.contextItems = clone(contextItems.value)
    current.updatedAt = Date.now()
    persistSessions()
  }

  function createSession(title?: string) {
    const session = createSessionRecord(title)
    sessions.value.unshift(session)
    sessionId.value = session.id
    messages.value = []
    references.value = []
    graphData.value = null
    contextItems.value = []
    currentAssistantContent.value = ''
    isStreaming.value = false
    persistSessions()
  }

  function switchSession(id: string) {
    if (isStreaming.value) return
    sessionId.value = id
    hydrateSession(id)
  }

  function renameSession(id: string, title: string) {
    const session = sessions.value.find((item) => item.id === id)
    if (!session || !title.trim()) return
    session.title = title.trim()
    session.updatedAt = Date.now()
    persistSessions()
  }

  function deleteSession(id: string) {
    const nextSessions = sessions.value.filter((item) => item.id !== id)
    sessions.value = nextSessions.length ? nextSessions : [createSessionRecord()]
    if (sessionId.value === id) {
      sessionId.value = sessions.value[0].id
      hydrateSession(sessionId.value)
    }
    persistSessions()
  }

  function addUserMessage(content: string) {
    messages.value.push({
      role: 'user',
      content,
      timestamp: Date.now(),
    })
    const current = sessions.value.find((item) => item.id === sessionId.value)
    if (current && current.title === '新问答会话' && messages.value.length <= 1) {
      current.title = content.slice(0, 18)
    }
    syncCurrentSession()
  }

  function startStreaming() {
    isStreaming.value = true
    currentAssistantContent.value = ''
    references.value = []
    graphData.value = null
  }

  function appendStreamChunk(text: string) {
    currentAssistantContent.value += text
  }

  function addReference(ref_: QAReference) {
    const nextKey = getReferenceKey(ref_)
    const existing = references.value.find((item) => getReferenceKey(item) === nextKey)
    if (existing) {
      Object.assign(existing, ref_)
      return
    }
    references.value.push(ref_)
  }

  function setGraphData(data: GraphData) {
    graphData.value = data
  }

  function finishStreaming() {
    if (currentAssistantContent.value) {
      messages.value.push({
        role: 'assistant',
        content: currentAssistantContent.value,
        references: [...references.value],
        graphData: graphData.value ? { ...graphData.value } : undefined,
        timestamp: Date.now(),
      })
    }
    isStreaming.value = false
    currentAssistantContent.value = ''
    syncCurrentSession()
  }

  function addContextItems(items: BasketItem[]) {
    const existingKeys = new Set(contextItems.value.map((item) => getBasketItemKey(item)))
    const additions = items.filter((item) => !existingKeys.has(getBasketItemKey(item)))
    if (!additions.length) return 0
    contextItems.value = [...contextItems.value, ...clone(additions)]
    syncCurrentSession()
    return additions.length
  }

  function removeContextItem(item: BasketItem) {
    const key = getBasketItemKey(item)
    contextItems.value = contextItems.value.filter((existing) => getBasketItemKey(existing) !== key)
    syncCurrentSession()
  }

  function getSeedDocIds(): string[] {
    const ordered: string[] = []
    contextItems.value.forEach((item) => {
      if (item.item_type === 'document' || item.item_type === 'snippet') {
        ordered.push(item.doc_id)
      } else if (item.item_type === 'matter') {
        ordered.push(...(item.governing_doc_ids || []))
      } else if (item.item_type === 'answer') {
        ordered.push(...(item.references || []).map((ref) => ref.doc_id))
      }
    })
    return [...new Set(ordered)]
  }

  if (sessionId.value) {
    hydrateSession(sessionId.value)
  }

  return {
    sessions,
    messages,
    references,
    graphData,
    contextItems,
    isStreaming,
    sessionId,
    currentAssistantContent,
    createSession,
    switchSession,
    renameSession,
    deleteSession,
    addUserMessage,
    startStreaming,
    appendStreamChunk,
    addReference,
    setGraphData,
    finishStreaming,
    addContextItems,
    removeContextItem,
    getSeedDocIds,
  }
})