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

const GENTLE_REMINDER_MAX_PER_HOUR = 5
const GENTLE_REMINDER_WINDOW_MS = 60 * 60 * 1000 // 1 hour

/**
 * Sends a gentle nudge from one partner to the other when the caller has
 * already answered today's question but the partner hasn't.
 *
 * Rate limits:
 *  - Per-user: max 5 gentle reminders per rolling hour. Guarded by a server-side
 *    transaction on `rate_limits/{uid}_gentle_reminder` so malicious clients
 *    cannot bypass it by calling the callable in a loop. The Android-side
 *    NotificationRateLimiter remains for UX but is not the authoritative guard.
 *  - Per-couple: one reminder per couple per calendar day (UTC). The lock is
 *    stored in couples/{coupleId}/gentle_reminders/{date} so it survives
 *    function restarts and is visible to both partners.
 *
 * The notification is both an FCM push (for the system tray) and an entry in
 * the partner's notification_queue (for in-app display).
 */
export const sendGentleReminderCallable = functions.https.onCall(async (_data, context) => {
  const callerId = context.auth?.uid
  if (!callerId) {
    throw new functions.https.HttpsError('unauthenticated', 'Must be signed in.')
  }
  if (!context.app) {
    throw new functions.https.HttpsError('failed-precondition', 'App Check verification required.')
  }

  const db = admin.firestore()

  // ── 1. Resolve couple + partner ──────────────────────────────────────────

  const userDoc = await db.collection('users').doc(callerId).get()
  const coupleId = userDoc.data()?.coupleId as string | undefined
  if (!coupleId) {
    throw new functions.https.HttpsError('failed-precondition', 'Not in a couple.')
  }

  const coupleDoc = await db.collection('couples').doc(coupleId).get()
  if (!coupleDoc.exists) {
    throw new functions.https.HttpsError('not-found', 'Couple not found.')
  }

  const userIds = (coupleDoc.data()?.userIds ?? []) as string[]
  const partnerId = userIds.find((id) => id !== callerId)
  if (!partnerId) {
    throw new functions.https.HttpsError('failed-precondition', 'No partner found.')
  }

  // ── 2. Server-side per-user throttle: 5 per hour (rolling window) ────────

  const now = admin.firestore.Timestamp.now()
  const rateLimitRef = db.collection('rate_limits').doc(`${callerId}_gentle_reminder`)

  const throttleResult = await db.runTransaction(async (tx) => {
    const snap = await tx.get(rateLimitRef)
    const data = snap.data()

    let windowStart: admin.firestore.Timestamp
    let count: number

    if (!snap.exists || !data) {
      windowStart = now
      count = 0
    } else {
      windowStart = data.windowStart as admin.firestore.Timestamp
      count = (typeof data.count === 'number' ? data.count : 0)

      const elapsedMs = now.toMillis() - windowStart.toMillis()
      if (elapsedMs >= GENTLE_REMINDER_WINDOW_MS) {
        // Rolling window has expired; start a fresh one.
        windowStart = now
        count = 0
      }
    }

    if (count >= GENTLE_REMINDER_MAX_PER_HOUR) {
      const retryAfterMs = GENTLE_REMINDER_WINDOW_MS - (now.toMillis() - windowStart.toMillis())
      const retryAfterMinutes = Math.max(1, Math.ceil(retryAfterMs / 60_000))
      return { allowed: false, retryAfterMinutes }
    }

    tx.set(rateLimitRef, {
      count: count + 1,
      windowStart,
      updatedAt: now,
    }, { merge: true })

    return { allowed: true, count: count + 1, windowStart }
  })

  if (!throttleResult.allowed) {
    throw new functions.https.HttpsError(
      'resource-exhausted',
      `Too many gentle reminders. Try again in ${throttleResult.retryAfterMinutes} minutes.`
    )
  }

  // ── 3. Rate limit: one per couple per day ────────────────────────────────

  const today = new Date().toISOString().slice(0, 10) // e.g. "2026-06-19"
  const lockRef = db
    .collection('couples')
    .doc(coupleId)
    .collection('gentle_reminders')
    .doc(today)

  const existingLock = await lockRef.get()
  if (existingLock.exists) {
    return { sent: false, reason: 'already_sent_today' }
  }

  // ── 4. Collect partner FCM tokens ────────────────────────────────────────

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

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

  // ── 5. Write in-app notification record ──────────────────────────────────

  await db
    .collection('users')
    .doc(partnerId)
    .collection('notification_queue')
    .add({
      type: 'gentle_reminder',
      title: 'Your partner is thinking about you.',
      body: "They left tonight's question open. Answer when you're ready.",
      read: false,
      sent: false,
      createdAt: admin.firestore.FieldValue.serverTimestamp(),
    })

  // ── 6. Claim the daily rate-limit lock ───────────────────────────────────

  await lockRef.set({
    sentBy: callerId,
    sentAt: admin.firestore.FieldValue.serverTimestamp(),
  })

  // ── 7. Send FCM push ─────────────────────────────────────────────────────

  if (tokens.length > 0) {
    const sendResults = await Promise.allSettled(
      tokens.map((token) =>
        admin.messaging().send({
          token,
          notification: {
            title: 'Your partner is thinking about you.',
            body: "They left tonight's question open. Answer when you're ready.",
          },
          data: {
            type: 'gentle_reminder',
            couple_id: coupleId,
          },
        })
      )
    )

    sendResults.forEach((result, i) => {
      if (result.status === 'rejected') {
        console.warn(
          `[sendGentleReminderCallable] FCM failed for token ${tokens[i]}:`,
          result.reason
        )
      }
    })
  }

  console.log(
    `[sendGentleReminderCallable] reminder sent from ${callerId} to ${partnerId} in couple ${coupleId}`
  )
  return { sent: true }
})
