import { computed, ref } from 'vue'

import { chatApi, type ChatMessage, type Conversation } from '@/api/chat'
import {
  startChatStream,
  type ChatStreamEvent,
  type ChatStreamHandle,
} from '@/composables/useChatStream'

interface UsagePayload {
  PromptTokens?: number
  CompletionTokens?: number
  ThinkingTokens?: number
}

interface TextDeltaPayload {
  text?: string
}

interface ErrorPayload {
  code?: string
  message?: string
}

interface MetaPayload {
  id?: number
  role?: string
  status?: ChatMessage['status']
}

// useConversationSession 承载"单个会话的消息流"全部状态与操作（打开/分页/发送/
// 取消/重试/SSE 流式接收）。每次调用返回一个独立实例，互不串扰——全局 /chat 页
// 经 chat store 持有一份，需求详情页内嵌面板各自持有一份。
// 调用方负责在组件卸载时调用 closeCurrent() 终止流。
export function useConversationSession() {
  const currentConversation = ref<Conversation | null>(null)
  const messages = ref<ChatMessage[]>([])
  const streamingMessageId = ref<number | null>(null)
  const streamHandle = ref<ChatStreamHandle | null>(null)

  const isStreaming = computed(() => streamingMessageId.value !== null)

  async function openConversation(id: string) {
    detachStream()
    const detail = await chatApi.getConversation(id)
    currentConversation.value = detail.conversation
    // 后端按 id DESC 返回（最新在前）；视图按升序渲染（最新在底部），故排为升序。
    messages.value = [...detail.messages].sort((a, b) => a.id - b.id)
    const pending = detail.messages.find((m) => m.status === 'pending')
    if (pending) {
      streamingMessageId.value = pending.id
      attachStream(`/api/v1/conversations/${id}/stream`, pending.id - 1)
    }
  }

  async function loadOlderMessages() {
    const conv = currentConversation.value
    if (!conv) return
    if (messages.value.length === 0) return
    const beforeId = messages.value.reduce(
      (min, m) => (m.id < min ? m.id : min),
      messages.value[0].id,
    )
    const older = await chatApi.listMessages(conv.id, { before_id: beforeId })
    if (older.length === 0) return
    const existingIds = new Set(messages.value.map((m) => m.id))
    const fresh = older.filter((m) => !existingIds.has(m.id))
    if (fresh.length === 0) return
    // 合并后统一按 id 升序，避免后端 DESC 批次与现有升序列表错位。
    messages.value = [...fresh, ...messages.value].sort((a, b) => a.id - b.id)
  }

  async function sendMessage(content: string, attachmentIds: string[] = []) {
    if (!currentConversation.value) return
    if (streamingMessageId.value !== null) return
    const conv = currentConversation.value
    const now = new Date().toISOString()
    const userMsg: ChatMessage = {
      id: -1,
      conversation_id: conv.id,
      role: 'user',
      content,
      status: 'pending',
      created_at: now,
      updated_at: now,
    }
    messages.value.push(userMsg)
    try {
      const resp = await chatApi.sendMessage(conv.id, {
        content,
        attachment_ids: attachmentIds,
      })
      userMsg.id = resp.user_message_id
      userMsg.status = 'ok'
      messages.value.push({
        id: resp.assistant_message_id,
        conversation_id: conv.id,
        role: 'assistant',
        content: '',
        status: 'pending',
        created_at: now,
        updated_at: now,
      })
      streamingMessageId.value = resp.assistant_message_id
      attachStream(resp.stream_url, resp.user_message_id)
    } catch (e) {
      userMsg.status = 'error'
      userMsg.error_message = e instanceof Error ? e.message : 'send failed'
      throw e
    }
  }

  async function cancelMessage() {
    const id = streamingMessageId.value
    if (id == null) return
    await chatApi.cancelMessage(id)
    // 取消会 abort SSE，后端不再下发 done/error/message_meta 终止事件，
    // 故须在此手动收尾（对齐 done/error/onError 路径）：将仍处于 pending 的
    // 助手消息置为终态 'cancelled'，并清空 streamingMessageId 解除 isStreaming。
    const msg = messages.value.find((m) => m.id === id)
    if (msg && msg.status === 'pending') {
      msg.status = 'cancelled'
    }
    streamingMessageId.value = null
    detachStream()
  }

  async function retryMessage(messageId: number) {
    if (!currentConversation.value) return
    const resp = await chatApi.retryMessage(messageId)
    const idx = messages.value.findIndex((m) => m.id === messageId)
    if (idx >= 0) {
      const old = messages.value[idx]
      messages.value[idx] = {
        ...old,
        id: resp.assistant_message_id,
        content: '',
        thinking: null,
        status: 'pending',
        error_message: null,
      }
    }
    streamingMessageId.value = resp.assistant_message_id
    attachStream(resp.stream_url, resp.assistant_message_id - 1)
  }

  function closeCurrent() {
    detachStream()
    currentConversation.value = null
    messages.value = []
    streamingMessageId.value = null
  }

  function attachStream(streamUrl: string, sinceMessageID: number) {
    detachStream()
    streamHandle.value = startChatStream(streamUrl, sinceMessageID, {
      onEvent: (ev) => applyStreamEvent(ev),
      onClose: () => {
        streamHandle.value = null
      },
      onError: (err) => {
        console.error('chat stream error', err)
        const id = streamingMessageId.value
        if (id != null) {
          const msg = messages.value.find((m) => m.id === id)
          if (msg && msg.status === 'pending') {
            msg.status = 'error'
            msg.error_message = err.message
          }
        }
        streamingMessageId.value = null
        streamHandle.value = null
      },
    })
  }

  function detachStream() {
    streamHandle.value?.stop()
    streamHandle.value = null
  }

  function applyStreamEvent(ev: ChatStreamEvent) {
    const targetId = streamingMessageId.value
    if (targetId == null) return
    const msg = messages.value.find((m) => m.id === targetId)
    if (!msg) return
    switch (ev.event) {
      case 'message_meta': {
        const d = ev.data as MetaPayload
        if (d.status) msg.status = d.status
        break
      }
      case 'text_delta': {
        const d = ev.data as TextDeltaPayload
        if (d.text) msg.content += d.text
        break
      }
      case 'thinking_delta': {
        const d = ev.data as TextDeltaPayload
        if (d.text) msg.thinking = (msg.thinking ?? '') + d.text
        break
      }
      case 'usage': {
        const u = ev.data as UsagePayload
        msg.prompt_tokens = u.PromptTokens ?? msg.prompt_tokens
        msg.completion_tokens = u.CompletionTokens ?? msg.completion_tokens
        msg.thinking_tokens = u.ThinkingTokens ?? msg.thinking_tokens
        break
      }
      case 'done':
        // 后端事件顺序：EventUsage → EventDone（参见 internal/chat/streamer.go）。
        // tokens 已在 'usage' 分支写入；某些 provider 不发 usage 时此处不会兜底。
        msg.status = 'ok'
        streamingMessageId.value = null
        break
      case 'error': {
        const d = ev.data as ErrorPayload
        msg.status = 'error'
        msg.error_message = d.message ?? d.code ?? 'stream error'
        streamingMessageId.value = null
        break
      }
    }
  }

  return {
    currentConversation,
    messages,
    streamingMessageId,
    isStreaming,
    openConversation,
    loadOlderMessages,
    sendMessage,
    cancelMessage,
    retryMessage,
    closeCurrent,
  }
}

export type ConversationSession = ReturnType<typeof useConversationSession>
