import {
  NativeModulesStatic,
  NativeModules,
} from 'react-native';
import { IBlazeResult } from './interfaces';
import { InitOptions } from './interfaces/init-options.interface';
import {
  PlayMomentOptions,
  PlayMomentsOptions,
  PlayStoriesOptions,
  PlayStoryOptions,
  PrepareMomentsOptions,
  PrepareStoriesOptions,
} from './interfaces/actions-options.interface';
import { BlazeGlobalDelegate, BlazeGlobalDelegateHelper } from './classes/global-delegate';
import { BlazeEntryPointDelegateHelper, BlazePlayerEntryPointDelegate } from './classes/entry-point-delegate';

interface BlazeSdkInterface {
  init(options: InitOptions): Promise<IBlazeResult>;
  playStory(options: PlayStoryOptions): Promise<void>;
  prepareStories(options: PrepareStoriesOptions): Promise<void>;
  playStories(options: PlayStoriesOptions): Promise<void>;
  playMoment(options: PlayMomentOptions): Promise<void>;
  prepareMoments(options: PrepareMomentsOptions): Promise<void>;
  playMoments(options: PlayMomentsOptions): Promise<void>;
  dismissPlayer(): Promise<void>;
  isInitialized: () => boolean;
  setExternalUserId(externalUserId?: string): Promise<void>;
  setDoNotTrack(doNotTrackUser: boolean): Promise<void>;
  handleUniversalLink(link: string): Promise<void>;
  canHandleUniversalLink(link: string): Promise<boolean>;
  updateGeoRestriction(geoLocation?: string): Promise<void>;
  setGlobalDelegate(globalDelegate?: BlazeGlobalDelegate | null): void;
  setEntryPointDelegate(playerEntryPointDelegate?: BlazePlayerEntryPointDelegate | null): void;
  canHandlePushNotification(payload: object): Promise<boolean>;
  handleNotificationPayload(payload: object): Promise<IBlazeResult>;
}

interface BlazeSdkInterfaceInternal extends NativeModulesStatic {
  init(options: InitOptions): Promise<IBlazeResult>;
  playStory(options: PlayStoryOptions): Promise<void>;
  prepareStories(options: PrepareStoriesOptions): Promise<void>;
  playStories(options: PlayStoriesOptions): Promise<void>;
  playMoment(options: PlayMomentOptions): Promise<void>;
  prepareMoments(options: PrepareMomentsOptions): Promise<void>;
  playMoments(options: PlayMomentsOptions): Promise<void>;
  dismissPlayer(): Promise<void>;
  isInitialized: () => boolean;
  setExternalUserId(externalUserId?: string): Promise<void>;
  setDoNotTrack(doNotTrackUser: boolean): Promise<void>;
  handleUniversalLink(link: string): Promise<void>;
  canHandleUniversalLink(link: string): Promise<boolean>;
  updateGeoRestriction(geoLocation?: string): Promise<void>;
  canHandlePushNotification(payload: object): Promise<boolean>;
  handleNotificationPayload(payload: object): Promise<IBlazeResult>;
}

const { RTNBlazeSdk } = NativeModules;
const BlazeSDKNativeModule = RTNBlazeSdk as BlazeSdkInterfaceInternal;

// This wrapper is hiding the internal interface that works with the native module, and serves as additional layer to add custom logic.
class BlazeSdkWrapper implements BlazeSdkInterface {

  init(options: InitOptions): Promise<IBlazeResult> {
    this.setEntryPointDelegate(options.playerEntryPointDelegate)
    this.setGlobalDelegate(options.globalDelegate)

    return BlazeSDKNativeModule.init(options);
  }

  isInitialized(): boolean {
    return BlazeSDKNativeModule.isInitialized();
  }

  playStory(options: PlayStoryOptions): Promise<void> {
    return BlazeSDKNativeModule.playStory(options);
  }

  prepareStories(options: PrepareStoriesOptions): Promise<void> {
    return BlazeSDKNativeModule.prepareStories(options);
  }

  playStories(options: PlayStoriesOptions): Promise<void> {
    return BlazeSDKNativeModule.playStories(options);
  }

  playMoment(options: PlayMomentOptions): Promise<void> {
    return BlazeSDKNativeModule.playMoment(options);
  }

  prepareMoments(options: PrepareMomentsOptions): Promise<void> {
    return BlazeSDKNativeModule.prepareMoments(options);
  }

  playMoments(options: PlayMomentsOptions): Promise<void> {
    return BlazeSDKNativeModule.playMoments(options);
  }

  dismissPlayer(): Promise<void> {
    return BlazeSDKNativeModule.dismissPlayer();
  }

  setExternalUserId(externalUserId?: string): Promise<void> {
    return BlazeSDKNativeModule.setExternalUserId(externalUserId);
  }

  setDoNotTrack(doNotTrackUser: boolean): Promise<void> {
    return BlazeSDKNativeModule.setDoNotTrack(doNotTrackUser);
  }

  handleUniversalLink(link: string): Promise<void> {
    return BlazeSDKNativeModule.handleUniversalLink(link);
  }

  canHandleUniversalLink(link: string): Promise<boolean> {
    return BlazeSDKNativeModule.canHandleUniversalLink(link);
  }

  updateGeoRestriction(geoLocation?: string): Promise<void> {
    return BlazeSDKNativeModule.updateGeoRestriction(geoLocation);
  }

  setGlobalDelegate(globalDelegate?: BlazeGlobalDelegate | null): void {
    BlazeGlobalDelegateHelper.registerGlobalDelegate(globalDelegate)
  }
  setEntryPointDelegate(playerEntryPointDelegate?: BlazePlayerEntryPointDelegate | null): void {
    BlazeEntryPointDelegateHelper.registerEntryPointDelegate(playerEntryPointDelegate)
  }

  canHandlePushNotification(payload: object): Promise<boolean> {
    return BlazeSDKNativeModule.canHandlePushNotification(payload)
  }

  handleNotificationPayload(payload: object): Promise<IBlazeResult> {
    return BlazeSDKNativeModule.handleNotificationPayload(payload)
  }

  // Singleton instance
  private static instance: BlazeSdkWrapper;

  // Private constructor for singleton
  private constructor() { }

  // Method to get singleton instance
  public static getInstance(): BlazeSdkWrapper {
    if (!this.instance) {
      this.instance = new BlazeSdkWrapper();
    }
    return this.instance;
  }

}

export const BlazeSDK = BlazeSdkWrapper.getInstance() as BlazeSdkInterface;
