import {
  TurboModuleRegistry,
  NativeModules,
  NativeEventEmitter,
} from 'react-native';
import type { Spec } from '../specs/NativeNamiFlowManager';
import type {
  NamiFlowHandoffPayload,
  NamiFlowHandoffStepHandler,
  NamiHandoffComplete,
  NamiHandoffOutcome,
} from './types';

const RNNamiFlowManager: Spec =
  TurboModuleRegistry.getEnforcing?.<Spec>('RNNamiFlowManager') ??
  NativeModules.RNNamiFlowManager;

const emitter = new NativeEventEmitter(NativeModules.RNNamiFlowManager);

export enum NamiFlowManagerEvents {
  Handoff = 'Handoff',
  FlowEvent = 'FlowEvent',
}

export const NamiFlowManager = {
  emitter,

  registerStepHandoff: (callback: NamiFlowHandoffStepHandler): (() => void) => {
    console.info('[NamiFlowManager] Registering step handoff listener...');

    const sub = emitter.addListener(
      NamiFlowManagerEvents.Handoff,
      (event: NamiFlowHandoffPayload) => {
        console.info('[NamiFlowManager] Received handoff event:', event);

        // Manufacture the per-handoff completion callback. The native event
        // carries a `handoffId` that correlates this call back to the native
        // pending handoff (TurboModule callbacks can't hold per-call closures).
        let completed = false;
        const complete: NamiHandoffComplete = (
          outcome?: NamiHandoffOutcome,
        ) => {
          if (completed) {
            console.warn(
              `[NamiFlowManager] complete() called more than once for handoff '${event.handoffTag}' — ignored`,
            );
            return;
          }
          completed = true;
          // The ONE bridge-side defaulting point: bare complete() ≙ done.
          // `outcome` stays required on the wire (native never sees a null).
          const normalized: NamiHandoffOutcome = outcome ?? { kind: 'done' };
          RNNamiFlowManager.completeHandoff?.(event.handoffId, normalized);
        };

        // Single handler shape `(tag, data, complete)`. Legacy `(tag, data)`
        // handlers still run — the extra arg is ignored — and their `resume()`
        // degrades to done-with-warning natively (the native pending token owns
        // that, surviving Metro fast-refresh wiping this closure).
        callback(event.handoffTag, event.handoffData, complete);
      },
    );

    RNNamiFlowManager.registerStepHandoff?.();

    return () => {
      sub.remove();
      RNNamiFlowManager.unregisterStepHandoff?.();
    };
  },

  resume: (): void => {
    console.info('[NamiFlowManager] resume from handoff requested');
    RNNamiFlowManager.resume?.();
  },

  pause: (): void => {
    console.info('[NamiFlowManager] pause from handoff requested');
    RNNamiFlowManager.pause();
  },

  registerEventHandler: (
    callback: (payload: Record<string, unknown>) => void,
  ): (() => void) => {
    const sub = emitter.addListener(NamiFlowManagerEvents.FlowEvent, callback);
    RNNamiFlowManager.registerEventHandler?.();
    return () => {
      sub.remove();
      RNNamiFlowManager.unregisterEventHandler?.();
    };
  },

  finish: (): void => {
    RNNamiFlowManager.finish();
  },

  isFlowOpen: async (): Promise<boolean> => {
    return RNNamiFlowManager.isFlowOpen();
  },
};
