import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

/**
 * Firestore trigger that notifies partners when a game session is created or completed.
 * 
 * Path: couples/{coupleId}/sessions/{sessionId}
 * Condition: onWrite (create, update, delete)
 */
export const onGameSessionUpdate = functions.firestore
  .document('couples/{coupleId}/sessions/{sessionId}')
  .onWrite(async (change, context) => {
    const { coupleId, sessionId } = context.params as { coupleId: string; sessionId: string }

    const db = admin.firestore()
    const messaging = admin.messaging()

    // Get the session document
    const sessionDoc = await db.collection('couples').doc(coupleId).collection('sessions').doc(sessionId).get()
    const session = sessionDoc.data()
    if (!session) {
      console.log(`[onGameSessionUpdate] session ${sessionId} not found, skipping`)
      return
    }

    // Get couple info
    const coupleDoc = await db.collection('couples').doc(coupleId).get()
    if (!coupleDoc.exists) {
      console.warn(`[onGameSessionUpdate] couple ${coupleId} not found`)
      return
    }

    const coupleData = coupleDoc.data() ?? {}
    const userIds = (coupleData.userIds ?? []) as string[]
    if (userIds.length !== 2) {
      console.warn(`[onGameSessionUpdate] invalid couple ${coupleId}: expected 2 users, got ${userIds.length}`)
      return
    }

    const partnerA = userIds[0]
    const partnerB = userIds[1]

    // Get user display names for notifications
    const userA = await db.collection('users').doc(partnerA).get()
    const userB = await db.collection('users').doc(partnerB).get()
    const partnerAName = userA.data()?.displayName ?? 'Partner A'
    const partnerBName = userB.data()?.displayName ?? 'Partner B'

    // Check if session was just created (status = "active")
    const previousData = change.before.data() ?? {}
    const currentData = change.after.data() ?? {}

    const wasInactive = (previousData.status ?? '') !== 'active'
    const isActiveNow = currentData.status === 'active'

    if (wasInactive && isActiveNow) {
      // New session started - notify the other partner
      const startedBy = currentData.startedByUserId
      const gameType = currentData.gameType ?? 'wheel'
      const partnerId = startedBy === partnerA ? partnerB : partnerA
      const partnerName = startedBy === partnerA ? partnerBName : partnerAName

      await notifyPartner(
        db,
        messaging,
        partnerId,
        partnerName,
        gameType,
        'partner_started_game',
        `${partnerName} has started a game. Tap to join!`
      )
      return
    }

    // Check if session was completed
    const wasActive = (previousData.status ?? '') === 'active'
    const isCompletedNow = currentData.status === 'completed'

    if (wasActive && isCompletedNow) {
      const completedBy = currentData.startedByUserId
      const partnerId = completedBy === partnerA ? partnerB : partnerA
      const completingPartnerName = completedBy === partnerA ? partnerAName : partnerBName

      // Check if partner has also completed
      const partnerCompletedAt = currentData.partnerCompletedAt
      if (partnerCompletedAt) {
        // Both completed - notify both
        await notifyPartner(
          db,
          messaging,
          partnerA,
          partnerAName,
          currentData.gameType ?? 'wheel',
          'partner_finished_game',
          `${partnerBName} has finished the game. Tap to see the results!`
        )
        await notifyPartner(
          db,
          messaging,
          partnerB,
          partnerBName,
          currentData.gameType ?? 'wheel',
          'partner_finished_game',
          `${partnerAName} has finished the game. Tap to see the results!`
        )
      } else {
        // Only one completed - notify the other to continue
        await notifyPartner(
          db,
          messaging,
          partnerId,
          completingPartnerName,
          currentData.gameType ?? 'wheel',
          'partner_finished_game',
          `${completingPartnerName} has finished. Tap to continue playing!`
        )
      }
      return
    }
  })

/**
 * Send notification to partner via FCM and write to notification_queue.
 */
async function notifyPartner(
  db: admin.firestore.Firestore,
  messaging: admin.messaging.Messaging,
  partnerId: string,
  partnerName: string,
  gameType: string,
  notificationType: string,
  body: string
): Promise<void> {
  const notificationPayload = {
    type: notificationType,
    title: `${partnerName} is playing`,
    body: body,
  }

  // Write an in-app notification record for the partner
  await db
    .collection('users')
    .doc(partnerId)
    .collection('notification_queue')
    .add({
      ...notificationPayload,
      read: false,
      createdAt: admin.firestore.FieldValue.serverTimestamp(),
    })

  // Collect the partner's FCM tokens
  const tokens: string[] = []
  const partnerUserDoc = await db.collection('users').doc(partnerId).get()
  
  if (partnerUserDoc.exists) {
    const legacyToken = partnerUserDoc.data()?.fcmToken
    if (typeof legacyToken === 'string' && legacyToken.length > 0) {
      tokens.push(legacyToken)
    }
  }

  const tokenSnapshot = await db
    .collection('users')
    .doc(partnerId)
    .collection('fcmTokens')
    .get()
  tokenSnapshot.docs.forEach((doc) => {
    const t = doc.data()?.token
    if (typeof t === 'string' && t.length > 0 && !tokens.includes(t)) {
      tokens.push(t)
    }
  })

  if (tokens.length === 0) {
    console.log(`[notifyPartner] no FCM tokens for ${partnerId}`)
    return
  }

  const fcmMessage: admin.messaging.Message = {
    token: tokens[0],
    notification: {
      title: notificationPayload.title,
      body: notificationPayload.body,
    },
    data: {
      type: notificationPayload.type,
      gameType: gameType,
      partnerId: partnerId,
    },
  }

  const sendResults = await Promise.allSettled(
    tokens.map((token) => messaging.send({ ...fcmMessage, token }))
  )

  const failures: string[] = []
  sendResults.forEach((result, index) => {
    if (result.status === 'rejected') {
      failures.push(`${tokens[index]}: ${String(result.reason)}`)
    }
  })

  if (failures.length > 0) {
    console.error(`[notifyPartner] some notifications failed:`, failures)
  } else {
    console.log(`[notifyPartner] notified ${partnerId} (${notificationType})`)
  }
}
