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

/**
 * Cloud Function: scheduledOutcomesReminder
 *
 * Manual test flow:
 *  1. Create a test couple with createdAt = ~31 days ago.
 *  2. Ensure no day_0/day_30 outcomes exist (or only day_0 exists to test day_30).
 *  3. Use the Firebase Functions shell or temporarily change the schedule to test.
 *  4. Verify FCM notification sent and notification_queue records written.
 *  5. Verify day_30 not sent if day_30 outcome already exists.
 *
 * Cron: every 24 hours. Iterates couples and nudges both members when a 30/60/90
 * day outcome is due and has not yet been submitted.
 */

export type OutcomeDayKey = 'day_0' | 'day_30' | 'day_60' | 'day_90'

const DAY_MS = 24 * 60 * 60 * 1000
const REMINDER_DAYS = [30, 60, 90] as const
const DAY_KEY_MAP: Record<number, OutcomeDayKey> = { 30: 'day_30', 60: 'day_60', 90: 'day_90' }

export const scheduledOutcomesReminder = functions.pubsub
  .schedule('every 24 hours')
  .onRun(async () => {
    const db = admin.firestore()
    const messaging = admin.messaging()
    const now = Date.now()

    const couplesSnap = await db.collection('couples').limit(200).get()
    const notifications: {
      userId: string
      coupleId: string
      day: number
      title: string
      body: string
    }[] = []

    for (const coupleDoc of couplesSnap.docs) {
      const coupleId = coupleDoc.id
      const data = coupleDoc.data() ?? {}
      const createdAt = millisFromFirestoreValue(data.createdAt)
      if (createdAt <= 0) continue

      const ageDays = Math.floor((now - createdAt) / DAY_MS)
      const dueDays = REMINDER_DAYS.filter((day) => ageDays >= day && ageDays <= day + 2)
      if (dueDays.length === 0) continue

      const userIds = (data.userIds ?? []) as string[]
      if (userIds.length === 0) continue

      // Check each due checkpoint; only remind for the first one without an outcome.
      let remindedDay: number | null = null
      for (const day of dueDays) {
        const dayKey = DAY_KEY_MAP[day]
        const outcomeSnap = await coupleDoc.ref.collection('outcomes').doc(dayKey).get()
        if (!outcomeSnap.exists) {
          remindedDay = day
          break
        }
      }
      if (remindedDay == null) continue

      const dayLabel = remindedDay
      const title = 'How are you feeling together?'
      const body = `You’ve been connected for ${dayLabel} days. Take a quick check-in to see how things have changed.`

      for (const userId of userIds) {
        notifications.push({ userId, coupleId, day: dayLabel, title, body })
      }
    }

    await Promise.all(
      notifications.map((notification) =>
        sendOutcomeReminder(db, messaging, notification)
      )
    )

    console.log(`[scheduledOutcomesReminder] scanned ${couplesSnap.size}; notified ${notifications.length}`)
  })

function millisFromFirestoreValue(value: unknown): number {
  if (typeof value === 'number') return value
  if (value instanceof admin.firestore.Timestamp) return value.toMillis()
  if (
    value != null &&
    typeof (value as { toMillis?: unknown }).toMillis === 'function'
  ) {
    return (value as { toMillis: () => number }).toMillis()
  }
  return 0
}

async function sendOutcomeReminder(
  db: admin.firestore.Firestore,
  messaging: admin.messaging.Messaging,
  notification: { userId: string; coupleId: string; day: number; title: string; body: string }
): Promise<void> {
  await db
    .collection('users')
    .doc(notification.userId)
    .collection('notification_queue')
    .add({
      type: 'outcome_reminder',
      title: notification.title,
      body: notification.body,
      coupleId: notification.coupleId,
      day: notification.day,
      read: false,
      createdAt: admin.firestore.FieldValue.serverTimestamp(),
    })

  const tokens = await getUserTokens(db, notification.userId)
  if (tokens.length === 0) {
    console.log(`[sendOutcomeReminder] no FCM tokens for ${notification.userId}`)
    return
  }

  const message: admin.messaging.Message = {
    token: tokens[0],
    notification: {
      title: notification.title,
      body: notification.body,
    },
    data: {
      type: 'outcome_reminder',
      coupleId: notification.coupleId,
      day: String(notification.day),
    },
  }

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

  sendResults.forEach((result, index) => {
    if (result.status === 'rejected') {
      console.warn(
        `[sendOutcomeReminder] FCM send to ${tokens[index]} failed:`,
        result.reason
      )
    }
  })
}

async function getUserTokens(db: admin.firestore.Firestore, userId: string): Promise<string[]> {
  const tokens: string[] = []
  const userDoc = await db.collection('users').doc(userId).get()
  const legacyToken = userDoc.data()?.fcmToken
  if (typeof legacyToken === 'string' && legacyToken.length > 0) {
    tokens.push(legacyToken)
  }

  const tokenSnapshot = await db
    .collection('users')
    .doc(userId)
    .collection('fcmTokens')
    .get()

  tokenSnapshot.docs.forEach((doc) => {
    const token = doc.data()?.token
    if (typeof token === 'string' && token.length > 0 && !tokens.includes(token)) {
      tokens.push(token)
    }
  })

  return tokens
}
