const cloud = require('wx-server-sdk')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
const _ = db.command
const SERVER_MESSAGE_LIMIT = 500
const CONVERSATIONS_COLLECTION = 'conversations'
const MESSAGES_COLLECTION = 'messages'
const CHAT_MESSAGE_TEMPLATE_ID = process.env.WQ_MESSAGE_TEMPLATE_ID || process.env.TARO_APP_MESSAGE_TEMPLATE_ID || ''

function isCollectionMissing(error) {
  const text = String(error?.errMsg || error?.message || error || '')
  return (
    text.includes('collection not exist') ||
    text.includes('collection not exists') ||
    text.includes('collection is not exists') ||
    text.includes('DATABASE_COLLECTION_NOT_EXIST') ||
    text.includes('-502005')
  )
}

function isCollectionAlreadyExists(error) {
  const text = String(error?.errMsg || error?.message || error || '')
  return text.includes('collection already exists') || text.includes('DATABASE_COLLECTION_ALREADY_EXIST')
}

async function ensureCollection(name) {
  if (typeof db.createCollection !== 'function') {
    throw new Error(`${name} collection does not exist`)
  }

  try {
    await db.createCollection(name)
  } catch (error) {
    if (!isCollectionAlreadyExists(error)) throw error
  }
}

async function getExistingConversation(openid, toOpenid) {
  try {
    return await db.collection(CONVERSATIONS_COLLECTION)
      .where({ type: 'single', memberOpenids: _.all([openid, toOpenid]) })
      .limit(1)
      .get()
  } catch (error) {
    if (!isCollectionMissing(error)) throw error
    await ensureCollection(CONVERSATIONS_COLLECTION)
    return { data: [] }
  }
}

async function getConversationById(conversationId) {
  try {
    return await db.collection(CONVERSATIONS_COLLECTION).doc(conversationId).get()
  } catch (error) {
    if (!isCollectionMissing(error)) throw error
    return { data: null }
  }
}

async function addConversation(data) {
  try {
    return await db.collection(CONVERSATIONS_COLLECTION).add({ data })
  } catch (error) {
    if (!isCollectionMissing(error)) throw error
    await ensureCollection(CONVERSATIONS_COLLECTION)
    return db.collection(CONVERSATIONS_COLLECTION).add({ data })
  }
}

async function addMessage(data) {
  try {
    return await db.collection(MESSAGES_COLLECTION).add({ data })
  } catch (error) {
    if (!isCollectionMissing(error)) throw error
    await ensureCollection(MESSAGES_COLLECTION)
    return db.collection(MESSAGES_COLLECTION).add({ data })
  }
}

async function trimConversationMessages(conversationId) {
  for (;;) {
    const oldMessages = await db.collection(MESSAGES_COLLECTION)
      .where({ conversationId })
      .orderBy('createdAt', 'desc')
      .skip(SERVER_MESSAGE_LIMIT)
      .limit(100)
      .get()
      .catch(() => ({ data: [] }))

    if (!oldMessages.data.length) return

    await Promise.all(
      oldMessages.data.map(item =>
        db.collection(MESSAGES_COLLECTION).doc(item._id).remove().catch(() => null)
      )
    )

    if (oldMessages.data.length < 100) return
  }
}

function pad(value) {
  return String(value).padStart(2, '0')
}

function formatDateTime(ts) {
  const d = new Date(ts)
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}

function templateValue(value, max = 20) {
  const text = String(value || '').trim()
  return text.length > max ? `${text.slice(0, max - 1)}…` : text
}

async function sendChatNotifications({ recipients, conversationId, senderName, content, type, createdAt }) {
  if (!CHAT_MESSAGE_TEMPLATE_ID || !recipients.length || !cloud.openapi?.subscribeMessage?.send) return

  const preview = type === 'image' ? '[图片]' : content
  await Promise.all(
    recipients.map(touser =>
      cloud.openapi.subscribeMessage.send({
        touser,
        templateId: CHAT_MESSAGE_TEMPLATE_ID,
        page: `pages/chat/index?conversationId=${conversationId}`,
        data: {
          thing31: { value: templateValue(senderName || '汪友') },
          thing2: { value: templateValue(preview || '你收到一条新消息') },
          time3: { value: formatDateTime(createdAt) }
        }
      }).catch(error => {
        console.log('[messageSend] subscribe message skipped', error?.errMsg || error?.message || error)
      })
    )
  )
}

exports.main = async event => {
  const { OPENID } = cloud.getWXContext()
  const content = String(event.content || '').trim()
  if (!content) throw new Error('content is required')
  if (!OPENID) throw new Error('no openid in wx context')

  const now = Date.now()
  let convId = event.conversationId
  let conversation = null

  // Create a 1:1 conversation if not provided
  if (!convId) {
    let { toOpenid } = event
    // Allow opening by user id without exposing openids to the client.
    if (!toOpenid && event.toUserId) {
      const toUserById = await db.collection('users').doc(event.toUserId).get().catch(() => null)
      if (toUserById && toUserById.data) toOpenid = toUserById.data.openid
      if (!toOpenid) {
        const toUserByOpenid = await db.collection('users')
          .where({ openid: event.toUserId })
          .limit(1)
          .get()
          .catch(() => ({ data: [] }))
        if (toUserByOpenid.data[0]) toOpenid = toUserByOpenid.data[0].openid
      }
    }
    if (!toOpenid) throw new Error('target user not found')

    // Look for existing conversation between these two users
    const existing = await getExistingConversation(OPENID, toOpenid)

    if (existing.data.length) {
      conversation = existing.data[0]
      convId = conversation._id
    } else {
      const toUserRes = await db.collection('users').where({ openid: toOpenid }).limit(1).get()
      const toUser = toUserRes.data[0] || { nickname: '用户', avatarKey: 'gradient-avatar-1' }
      conversation = {
        type: 'single',
        memberOpenids: [OPENID, toOpenid],
        unreadOpenids: [],
        unreadCounts: {},
        title: toUser.nickname,
        avatarKey: toUser.avatarKey,
        lastMessage: { content: content.slice(0, 50), createdAt: now },
        pinned: false,
        muted: false,
        createdAt: now,
        updatedAt: now
      }
      const newConv = await addConversation(conversation)
      convId = newConv._id
    }
  }

  if (!conversation) {
    const convRes = await getConversationById(convId)
    conversation = convRes.data
  }
  if (!conversation) throw new Error('conversation not found')

  const members = conversation.memberOpenids || []
  if (!members.includes(OPENID)) throw new Error('permission denied')
  if ((conversation.type || 'single') !== 'single') throw new Error('unsupported conversation type')

  // Add message to messages collection
  const msgResult = await addMessage({
    conversationId: convId,
    fromOpenid: OPENID,
    content: content.slice(0, 1000),
    type: event.msgType || 'text',
    createdAt: now
  })

  // Update conversation: last message + unread counts for all recipients
  const recipients = members.filter(id => id !== OPENID)

  const updateData = {
    lastMessage: { content: content.slice(0, 50), createdAt: now },
    updatedAt: now
  }
  // Add recipients to unreadOpenids and increment their unread counts
  recipients.forEach(r => {
    updateData[`unreadCounts.${r}`] = _.inc(1)
  })

  // Rebuild unreadOpenids to include all recipients (remove duplicates)
  const existingUnread = conversation.unreadOpenids || []
  const newUnread = [...new Set([...existingUnread, ...recipients])]
  updateData.unreadOpenids = newUnread

  await db.collection(CONVERSATIONS_COLLECTION).doc(convId).update({ data: updateData })
  await trimConversationMessages(convId)
  const senderRes = await db.collection('users').where({ openid: OPENID }).limit(1).get().catch(() => ({ data: [] }))
  await sendChatNotifications({
    recipients,
    conversationId: convId,
    senderName: senderRes.data[0]?.nickname || conversation.title || '汪友',
    content,
    type: event.msgType || 'text',
    createdAt: now
  })

  return { messageId: msgResult._id, conversationId: convId }
}
