/**
 * Native FCM sender for staff chat (the Android app analog of pushNotifier.ts's web push).
 *
 * Lazy + fail-soft, exactly like fcmDb/chatDb: firebase-admin is initialized on first send from a
 * service-account credential, and if the credential is missing or invalid, every call no-ops with a
 * warning — native push degrades, the server never crashes at boot, and web push is untouched.
 *
 * Credential (a server secret — NEVER in the repo): provide ONE of
 *   - FCM_SERVICE_ACCOUNT_JSON  = the service-account JSON (raw or base64), or
 *   - FCM_SERVICE_ACCOUNT_FILE  = a path to the service-account JSON file.
 *
 * firebase-admin is stateless → does NOT affect the per-process presence (PM2 instances:1) constraint.
 */

import fs from 'fs'

let app: any = null
let initFailed = false

const loadServiceAccount = (): any | null => {
  try {
    const raw = process.env.FCM_SERVICE_ACCOUNT_JSON
    if (raw && raw.trim()) {
      const s = raw.trim()
      const json = s.startsWith('{') ? s : Buffer.from(s, 'base64').toString('utf-8')
      return JSON.parse(json)
    }
    const file = process.env.FCM_SERVICE_ACCOUNT_FILE
    if (file && fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf-8'))
  } catch (e: any) {
    console.warn('[fcm] service-account parse failed:', e?.message)
  }
  return null
}

const getApp = async () => {
  if (app) return app
  if (initFailed) return null
  try {
    const { getApps, initializeApp, cert } = await import('firebase-admin/app')
    const existing = getApps()
    if (existing.length) { app = existing[0]; return app }
    const svc = loadServiceAccount()
    if (!svc) { initFailed = true; console.warn('[fcm] no service account → native push disabled'); return null }
    app = initializeApp({ credential: cert(svc) })
    return app
  } catch (e: any) {
    initFailed = true
    console.warn('[fcm] init failed (native push disabled):', e?.message)
    return null
  }
}

/**
 * Send a chat notification to all of a user's registered devices. No tokens / no credential → no-op.
 * Dead tokens are pruned (FCM analog of web-push HTTP 410).
 */
export const sendFcmToUser = async (
  userId: number,
  msg: { title: string; body: string; data?: Record<string, any> },
) => {
  const a = await getApp()
  if (!a) { console.warn('[fcm] skip: no credential loaded (set FCM_SERVICE_ACCOUNT_JSON/_FILE)'); return }

  const { getFcmTokensForUser, deleteFcmToken } = await import('./fcmDb')
  const tokens = getFcmTokensForUser(userId)
  if (!tokens.length) { console.warn(`[fcm] skip: user ${userId} has no registered device tokens`); return }

  const data: Record<string, string> = {}
  for (const [k, v] of Object.entries(msg.data || {})) data[k] = String(v)

  try {
    const { getMessaging } = await import('firebase-admin/messaging')
    const res: any = await getMessaging(a).sendEachForMulticast({
      tokens,
      // BOTH blocks so a Doze/killed device still surfaces a notification and the data deep-links on tap.
      notification: { title: msg.title, body: msg.body },
      data,
      android: {
        priority: 'high',
        notification: {
          channelId: 'chat',
          tag: data.senderUserId ? `chat-${data.senderUserId}` : 'chat',
        },
      },
    })
    console.log(`[fcm] user ${userId}: sent ${res.successCount}/${tokens.length} (failed ${res.failureCount})`)
    res.responses.forEach((r: any, i: number) => {
      const code = r?.error?.code
      if (!r.success) {
        console.warn(`[fcm] user ${userId} token#${i} failed:`, code, r?.error?.message)
        if (code === 'messaging/registration-token-not-registered' || code === 'messaging/invalid-argument' || code === 'messaging/invalid-registration-token') {
          deleteFcmToken(tokens[i])
        }
      }
    })
  } catch (e: any) {
    console.warn('[fcm] send failed:', e?.message)
  }
}
