/**
 * Bridge zwischen CallKit/ConnectionService-Events und unserer Call-Store.
 *
 * Wenn der User in der nativen Call-UI Accept/Reject/Hangup tippt, kommt das
 * NICHT über unser React-UI rein — sondern via RNCallKeep-Events. Wir
 * übersetzen die in store-Actions.
 *
 * Wird einmal app-weit im _layout.tsx aufgerufen.
 */
import { useEffect } from 'react';
import { AppState, Platform } from 'react-native';
import { useRouter } from 'expo-router';
import { useCallStore } from '../stores/call';
// RNCallKeep kommt aus lib/callkit (guarded — null wenn das native Modul fehlt,
// z.B. im Dev-Build). NICHT direkt aus 'react-native-callkeep' importieren, sonst
// crasht der Import. Alle Aufrufe hier per optional chaining absichern.
import { setupCallKeep, RNCallKeep } from '../lib/callkit';

// VoIP-PushKit (iOS only) — Payload-Empfang um peer-Info in den Store zu
// hydrieren, BEVOR User in der CallKit-UI auf "Annehmen" tippt.
let RNVoipPushNotification: any = null;
if (Platform.OS === 'ios') {
  try {
    RNVoipPushNotification = require('react-native-voip-push-notification').default;
  } catch {
    // ungelinkt — skip
  }
}

export function useCallKeepEvents() {
  const router = useRouter();

  useEffect(() => {
    void setupCallKeep();

    // Zombie-Cleanup: verzögert + BEDINGT. Ein Phantom-Call aus einer Vorsession
    // (CallKit ohne sauberes endCall) soll beim Launch verschwinden — ABER bei
    // einem VoIP-Cold-Start startet der eingehende Call selbst die App. Dann
    // hydriert onVoipNotification/receiveIncoming den Store binnen ~1-2s auf
    // 'incoming' — diesen legitimen Call dürfen wir NICHT beenden (sonst
    // Auto-Reject bei Force-Quit). Darum erst nach Grace-Period aufräumen und NUR
    // wenn der Store keinen echten Call kennt (idle/ended).
    const zombieCleanup = setTimeout(() => {
      const st = useCallStore.getState().status;
      if (st === 'idle' || st === 'ended') {
        console.log('[callkeep] launch zombie-cleanup → endAllCalls (no live call in store)');
        try { RNCallKeep.endAllCalls(); } catch {}
      } else {
        console.log('[callkeep] launch zombie-cleanup SKIPPED (store status=' + st + ')');
      }
    }, 5000);

    // VoIP-Push-Payload (iOS) → Store hydrieren mit caller-Info, damit
    // acceptCall() den richtigen peer kennt. Der CallKit-UI-Show ist bereits
    // in AppDelegate.swift (reportNewIncomingCall) erfolgt.
    const onVoipNotification = (payload: any) => {
      console.log('[voip] incoming push payload', payload);
      const st = useCallStore.getState();
      const callId = String(payload?.callId ?? '');
      const from = payload?.from;
      if (!callId || !from?.id) return;
      // receiveIncoming dedupliziert via status-Check → safe doppelt zu rufen.
      st.receiveIncoming(callId, {
        id: String(from.id),
        nickname: String(from.nickname ?? payload?.callerName ?? 'ReBreak'),
        avatar: from.avatar ?? null,
      });
    };
    if (RNVoipPushNotification) {
      RNVoipPushNotification.addEventListener('notification', onVoipNotification);
      // didLoadWithEvents wird beim Cold-Start gefeuert mit den queued Events,
      // die vor dem ersten addEventListener-Call eingegangen sind.
      RNVoipPushNotification.addEventListener('didLoadWithEvents', (events: any[]) => {
        for (const ev of events ?? []) {
          if (ev?.name === 'RNVoipPushRemoteNotificationReceivedEvent') {
            onVoipNotification(ev?.data);
          }
        }
      });
    }

    // User tippt "Annehmen" in der CallKit-/ConnectionService-UI
    const onAnswer = ({ callUUID }: { callUUID: string }) => {
      const st = useCallStore.getState();
      console.log('[callkeep] answer', callUUID, 'appState=', AppState.currentState, 'storeStatus=', st.status);
      if (st.status !== 'incoming') {
        // CallKit hat den Call nativ bereits auf 'aktiv' gesetzt (Banner \u2192 Timer).
        // Ohne 'incoming'-State im Store k\u00f6nnen wir nicht joinen \u2192 es entst\u00fcnde ein
        // Phantom-Call der ewig \u201eaktiv" tickt. Sauber beenden statt nur return.
        console.log('[callkeep] answer but store not incoming \u2192 endAllCalls to avoid phantom');
        try { RNCallKeep.endAllCalls(); } catch {}
        return;
      }
      // Call-Screen \u00f6ffnen und Accept-Flow triggern.
      router.push('/call');
      void st.acceptCall();
    };

    // User tippt "Ablehnen" oder "Auflegen" in der nativen UI
    const onEnd = ({ callUUID }: { callUUID: string }) => {
      const appState = AppState.currentState;
      const st = useCallStore.getState();
      console.log('[callkeep] end', callUUID, 'appState=', appState, 'storeStatus=', st.status);
      if (st.status === 'idle' || st.status === 'ended') return;
      // iOS-Foreground: AppDelegate.didReceiveIncomingPush MUSS reportNewIncomingCall
      // aufrufen (Apple-Pflicht, sonst killt iOS die App). Im Foreground bedient
      // sich der User aber an unserem In-App /call-Screen mit Accept/Decline-
      // Buttons. CallKit's Auto-Dismiss-Timer (~5s im Foreground!) darf den
      // User-Flow NICHT unterbrechen — sonst verschwindet der /call-Screen nach
      // 1-5s ohne dass der User irgendwas getippt hat.
      // (Empirisch verifiziert: dieser early-return ist genau der Fix für das
      // "1-3s disappear"-Bug. Bitte nicht erneut entfernen.)
      if (Platform.OS === 'ios' && appState === 'active' && st.status === 'incoming') {
        console.log('[callkeep] endCall IGNORED (iOS foreground incoming — in-app UI is authoritative)');
        return;
      }
      if (st.status === 'incoming') {
        st.declineCall();
      } else {
        st.hangup('ended');
      }
    };

    // User mutet/unmutet über die native UI
    const onMuted = ({ muted }: { muted: boolean; callUUID: string }) => {
      const st = useCallStore.getState();
      if (st.muted !== muted) st.toggleMute();
    };

    RNCallKeep?.addEventListener('answerCall', onAnswer);
    RNCallKeep?.addEventListener('endCall', onEnd);
    RNCallKeep?.addEventListener('didPerformSetMutedCallAction', onMuted);
    // didActivateAudioSession kommt nach CallKit-Audio-Activation — wir nutzen
    // das (noch) nicht aktiv, weil WebRTC + InCallManager das selber regeln.

    return () => {
      clearTimeout(zombieCleanup);
      RNCallKeep?.removeEventListener('answerCall');
      RNCallKeep?.removeEventListener('endCall');
      RNCallKeep?.removeEventListener('didPerformSetMutedCallAction');
      if (RNVoipPushNotification) {
        RNVoipPushNotification.removeEventListener('notification');
        RNVoipPushNotification.removeEventListener('didLoadWithEvents');
      }
    };
  }, [router]);
}
