/**
 * PushService.ts — wake the app on an incoming call when the phone is locked.
 *
 * Two platform contracts, one goal: get the app registered to Asterisk FAST so
 * the INVITE from [ring-engineer] finds a live endpoint.
 *
 *   iOS   — Apple PushKit (VoIP push). Delivered even when app is killed. STRICT
 *           rule: you MUST call CallKit displayIncomingCall in the SAME handler
 *           tick, or iOS terminates the app. So we show native UI first, then
 *           register + wait for the INVITE.
 *
 *   Android — FCM high-priority "data" message. Wakes a headless JS task; we show
 *           the CallKeep incoming UI (or a full-screen-intent notification) and
 *           kick registration. From Android 8+ this needs a foreground service,
 *           which CallKeep's foregroundService config provides.
 *
 * Server side, Asterisk's [ring-engineer] runs `ephone-push.sh <ext> <clid>`.
 * That script looks up the engineer's device token and sends the push below.
 * Payload shape it must send:
 *   { callId, handle: "<caller number>", name: "<caller name>", ext: "6001" }
 */
import { Platform } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import { sipEngine } from './SipEngine';
import { callManager } from './CallManager';
import { getStoredSipConfig } from './authStore'; // Step 4 provides this

// iOS PushKit lives in a separate lib because it is NOT FCM.
let VoipPushNotification: any = null;
if (Platform.OS === 'ios') {
  VoipPushNotification = require('react-native-voip-push-notification').default;
}

interface CallPush {
  callId: string;
  handle: string;
  name: string;
  ext: string;
}

class PushService {
  async init(): Promise<void> {
    if (Platform.OS === 'ios') this.initIOS();
    else await this.initAndroid();
  }

  // ---------------------------------------------------------------------------
  // Device token registration — send this token to your backend so
  // ephone-push.sh knows where to push for extension 6001.
  // ---------------------------------------------------------------------------
  async syncToken(extension: string): Promise<void> {
    let token: string;
    if (Platform.OS === 'ios') {
      token = await new Promise((res) => {
        VoipPushNotification.addEventListener('register', (t: string) => res(t));
        VoipPushNotification.registerVoipToken();
      });
    } else {
      token = await messaging().getToken();
    }
    await fetch('https://api.yourdomain.com/devices/register', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ extension, platform: Platform.OS, token }),
    });
  }

  // ---------------------------------------------------------------------------
  // iOS PushKit
  // ---------------------------------------------------------------------------
  private initIOS(): void {
    VoipPushNotification.addEventListener('notification', (payload: any) => {
      const data: CallPush = payload; // apns custom keys arrive flat
      // 1) REPORT TO CALLKIT IMMEDIATELY — non-negotiable PushKit requirement.
      callManager.displayIncomingFromPush(data.callId, data.handle, data.name);
      // 2) Wake SIP so the real INVITE can be answered.
      this.wakeAndRegister();
    });
    // Some payloads arrive before JS listeners attach; drain the cache.
    VoipPushNotification.addEventListener('didLoadWithEvents', (events: any[]) => {
      events?.forEach((e) => {
        if (e.name === 'RNVoipPushRemoteNotificationReceivedEvent') {
          const data: CallPush = e.data;
          callManager.displayIncomingFromPush(data.callId, data.handle, data.name);
          this.wakeAndRegister();
        }
      });
    });
  }

  // ---------------------------------------------------------------------------
  // Android FCM — headless handler runs even when the app is backgrounded/killed.
  // Register this in index.js:  messaging().setBackgroundMessageHandler(...)
  // ---------------------------------------------------------------------------
  private async initAndroid(): Promise<void> {
    // Foreground data messages (app open):
    messaging().onMessage(async (msg) => this.handleAndroidPush(msg));
  }

  /** Exported so index.js's setBackgroundMessageHandler can reuse it. */
  async handleAndroidPush(msg: any): Promise<void> {
    const data: CallPush = msg?.data || {};
    if (!data.callId) return;
    callManager.displayIncomingFromPush(data.callId, data.handle, data.name);
    await this.wakeAndRegister();
  }

  // ---------------------------------------------------------------------------
  // Shared: ensure the UA is up and REGISTERed so the INVITE lands.
  // Asterisk Wait(2) in [ring-engineer] buys us the time this takes.
  // ---------------------------------------------------------------------------
  private async wakeAndRegister(): Promise<void> {
    if (sipEngine.isRegistered) {
      sipEngine.register(); // refresh binding just in case
      return;
    }
    const cfg = await getStoredSipConfig();
    if (cfg) await sipEngine.start(cfg);
  }
}

export const pushService = new PushService();
