import Taro from '@tarojs/taro'
import { callCloud, canUseCloud } from './cloud'
import { isCloudFileId, resolveCloudFileUrl, resolveCloudFileUrlMap } from './cloud-file'
import { mockActiveUsers, mockConversations } from './mock'
import { markStale } from '@/store/dataBus'
import type { ActiveUser, ChatMessage, ChatPeer, Conversation, MessageTab } from '@/types/domain'

interface MessageSendOptions {
  conversationId?: string
  toOpenid?: string
  toUserId?: string
  msgType?: string
}

interface GetThreadParams {
  conversationId?: string
  targetUserId?: string
  cursor?: string
  pageSize?: number
  markRead?: boolean
}

export interface MessageThreadResponse {
  conversationId: string
  peer: ChatPeer | null
  messages: ChatMessage[]
  nextCursor: string
}

async function resolveMessageListAssets(result: {
  activeUsers: ActiveUser[]
  conversations: Conversation[]
}): Promise<{ activeUsers: ActiveUser[]; conversations: Conversation[] }> {
  const urls = [
    ...result.activeUsers.map(user => user.avatarUrl || ''),
    ...result.conversations.map(conversation => conversation.avatarUrl || '')
  ]
  const urlMap = await resolveCloudFileUrlMap(urls)
  const readUrl = (url?: string) => {
    if (!url) return url
    return urlMap.has(url) ? urlMap.get(url) || '' : url
  }

  return {
    activeUsers: result.activeUsers.map(user => ({ ...user, avatarUrl: readUrl(user.avatarUrl) })),
    conversations: result.conversations.map(conversation => ({
      ...conversation,
      avatarUrl: readUrl(conversation.avatarUrl)
    }))
  }
}

async function resolveThreadAssets(result: MessageThreadResponse): Promise<MessageThreadResponse> {
  const urls = [
    result.peer?.avatarUrl || '',
    ...result.messages
      .filter(message => message.type === 'image')
      .map(message => message.content || '')
  ]
  const urlMap = await resolveCloudFileUrlMap(urls)
  const readUrl = (url?: string) => {
    if (!url) return url
    return urlMap.has(url) ? urlMap.get(url) || '' : url
  }

  return {
    ...result,
    peer: result.peer ? { ...result.peer, avatarUrl: readUrl(result.peer.avatarUrl) } : null,
    messages: result.messages.map(message => {
      if (message.type !== 'image') return message

      const rawContent = message.content || ''
      const fileId = message.fileId || (isCloudFileId(rawContent) ? rawContent : undefined)
      const resolvedContent = readUrl(rawContent) || (!isCloudFileId(rawContent) ? rawContent : '')

      return {
        ...message,
        fileId,
        content: resolvedContent,
        assetState: resolvedContent ? 'ready' : 'unresolved'
      }
    })
  }
}

export async function getMessages(tab: MessageTab): Promise<{
  activeUsers: ActiveUser[]
  conversations: Conversation[]
}> {
  // Sample data only in non-cloud previews (e.g. H5 dev).
  if (!canUseCloud()) {
    const conversations =
      tab === 'unread'
        ? mockConversations.filter(item => item.unreadCount > 0)
        : mockConversations

    return { activeUsers: mockActiveUsers, conversations }
  }

  // Real mini program: always reflect cloud data, even when empty.
  try {
    const result = await callCloud<object, { activeUsers: ActiveUser[]; conversations: Conversation[] }>('messageList', { tab })
    return resolveMessageListAssets({
      activeUsers: result.activeUsers || [],
      conversations: result.conversations || []
    })
  } catch {
    return { activeUsers: [], conversations: [] }
  }
}

export async function markConversationRead(conversationId: string): Promise<number> {
  // Unread counts changed — let the message list refresh next time it shows.
  markStale('messages')
  if (!canUseCloud()) return 0
  const result = await callCloud<object, { unreadCount: number }>('conversationRead', { conversationId })
  return result.unreadCount
}

export async function sendMessage(
  content: string,
  options: MessageSendOptions
): Promise<{ messageId: string; conversationId: string }> {
  if (!canUseCloud()) {
    return {
      messageId: `local_${Date.now()}`,
      conversationId: options.conversationId || (options.toUserId ? `local_${options.toUserId}` : 'local')
    }
  }
  const result = await callCloud<MessageSendOptions & { content: string }, { messageId?: string; conversationId?: string }>(
    'messageSend',
    { content, ...options }
  )
  if (!result?.messageId || !result?.conversationId) {
    throw new Error(`messageSend returned invalid result: ${JSON.stringify(result)}`)
  }
  // The conversation's last message / order changed — refresh the list on return.
  markStale('messages')
  return { messageId: result.messageId, conversationId: result.conversationId }
}

export async function uploadChatImage(filePath: string): Promise<string> {
  if (!canUseCloud() || filePath.startsWith('cloud://')) return filePath

  const ext = (filePath.split('?')[0].split('.').pop() || 'jpg').slice(0, 5)
  const cloudPath = `messages/${Date.now()}_${Math.random().toString(36).slice(2, 8)}.${ext}`
  const res = await Taro.cloud.uploadFile({ cloudPath, filePath })
  return res.fileID
}

export async function sendImageMessage(
  filePath: string,
  options: Omit<MessageSendOptions, 'msgType'>
): Promise<{ messageId: string; conversationId: string; content: string; fileId?: string; assetState?: 'ready' | 'unresolved' }> {
  const content = await uploadChatImage(filePath)
  const result = await sendMessage(content, { ...options, msgType: 'image' })
  const resolved = await resolveCloudFileUrl(content)
  return {
    ...result,
    fileId: isCloudFileId(content) ? content : undefined,
    content: resolved || filePath,
    assetState: resolved || filePath ? 'ready' : 'unresolved'
  }
}

export async function getThread(params: GetThreadParams): Promise<MessageThreadResponse> {
  if (!canUseCloud()) {
    return {
      conversationId: params.conversationId || '',
      peer: null,
      messages: [],
      nextCursor: ''
    }
  }
  try {
    const result = await callCloud<GetThreadParams, MessageThreadResponse>('messageThread', params)
    return resolveThreadAssets({
      conversationId: result.conversationId || params.conversationId || '',
      peer: result.peer || null,
      messages: result.messages || [],
      nextCursor: result.nextCursor || ''
    })
  } catch {
    return { conversationId: params.conversationId || '', peer: null, messages: [], nextCursor: '' }
  }
}
