import {
  NativeModulesStatic,
  NativeModules,
} from 'react-native';
import { InitOptions } from './interfaces/init-options.interface';
import {
  PlayMomentOptions,
  PlayMomentsOptions,
  PlayStoriesOptions,
  PlayStoryOptions,
  PlayVideoOptions,
  PlayVideosOptions,
  PrepareMomentsOptions,
  PrepareStoriesOptions,
  PrepareVideosOptions,
  AppendMomentsToPlayerOptions
} from './interfaces/actions-options.interface';
import { BlazeGlobalDelegate, BlazeGlobalDelegateHelper } from './classes/global-delegate';
import { BlazeEntryPointDelegateHelper, BlazePlayerEntryPointDelegate } from './classes/entry-point-delegate';
import { mapToBlazeErrorOrRethrow } from './interfaces/blaze-error.interface';
import { BlazeVideosPlaybackConfiguration } from './interfaces/videos-playback-configuration';
import { BlazeMomentsPlaybackConfiguration } from './interfaces/moments-playback-configuration';
import { BlazeFollowEntitiesDelegateHelper, BlazeFollowEntitiesDelegate } from './classes/follow-entities-delegate';
import { BlazeCastingDelegateHelper, BlazeCastingDelegate } from './classes/casting-delegate';
import { BlazePipDelegateHelper, BlazePipDelegate } from './classes/pip-delegate';
import { BlazeSearchScreenOptions } from './interfaces/search-screen-options';

interface BlazeSdkInterface {
  init(options: InitOptions): Promise<void>;
  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>;
  appendMomentsToPlayer(options: AppendMomentsToPlayerOptions): Promise<void>;
  playVideo(options: PlayVideoOptions): Promise<void>;
  prepareVideos(options: PrepareVideosOptions): Promise<void>;
  playVideos(options: PlayVideosOptions): Promise<void>;
  dismissPlayer(): Promise<void>;
  isInitialized: () => boolean;
  setExternalUserId(externalUserId?: string): Promise<void>;
  setDoNotTrack(doNotTrackUser: boolean): Promise<void>;
  setDisableAnalytics(disableAnalytics: 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;
  setDefaultVideosPlaybackConfiguration(config: BlazeVideosPlaybackConfiguration): Promise<void>;
  getDefaultVideosPlaybackConfiguration(): Promise<BlazeVideosPlaybackConfiguration>;
  setDefaultMomentsPlaybackConfiguration(config: BlazeMomentsPlaybackConfiguration): Promise<void>;
  getDefaultMomentsPlaybackConfiguration(): Promise<BlazeMomentsPlaybackConfiguration>;
  canHandlePushNotification(payload: object): Promise<boolean>;
  handleNotificationPayload(payload: object): Promise<void>;
  setFollowEntitiesDelegate(delegate?: BlazeFollowEntitiesDelegate | null): void;
  setFollowedEntities(entityIds: string[]): Promise<void>;
  insertFollowedEntities(entityIds: string[]): Promise<void>;
  removeFollowedEntities(entityIds: string[]): Promise<void>;
  getFollowedEntities(): Promise<string[]>;
  setPreferredLanguage(language: string | null): Promise<void>;
  stopActiveCastingSession(): Promise<void>;
  setCastingDelegate(delegate?: BlazeCastingDelegate | null): void;
  showSearchScreen(options?: BlazeSearchScreenOptions): Promise<void>;
  stopActivePiPSession(): Promise<void>;
  isPiPActive(): Promise<boolean>;
  setPipDelegate(delegate?: BlazePipDelegate | null): void;
}

interface BlazeSdkInterfaceInternal extends NativeModulesStatic {
  init(options: InitOptions): Promise<void>;
  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>;
  appendMomentsToPlayer(options: AppendMomentsToPlayerOptions): Promise<void>;
  playVideo(options: PlayVideoOptions): Promise<void>;
  prepareVideos(options: PrepareVideosOptions): Promise<void>;
  playVideos(options: PlayVideosOptions): Promise<void>;
  dismissPlayer(): Promise<void>;
  isInitialized: () => boolean;
  setExternalUserId(externalUserId?: string): Promise<void>;
  setDoNotTrack(doNotTrackUser: boolean): Promise<void>;
  setDisableAnalytics(disableAnalytics: boolean): Promise<void>;
  handleUniversalLink(link: string): Promise<void>;
  canHandleUniversalLink(link: string): Promise<boolean>;
  updateGeoRestriction(geoLocation?: string): Promise<void>;
  setDefaultVideosPlaybackConfiguration(config: BlazeVideosPlaybackConfiguration): Promise<void>;
  getDefaultVideosPlaybackConfiguration(): Promise<BlazeVideosPlaybackConfiguration>;
  setDefaultMomentsPlaybackConfiguration(config: BlazeMomentsPlaybackConfiguration): Promise<void>;
  getDefaultMomentsPlaybackConfiguration(): Promise<BlazeMomentsPlaybackConfiguration>;
  canHandlePushNotification(payload: object): Promise<boolean>;
  handleNotificationPayload(payload: object): Promise<void>;
  setFollowedEntities(entityIds: string[]): Promise<void>;
  insertFollowedEntities(entityIds: string[]): Promise<void>;
  removeFollowedEntities(entityIds: string[]): Promise<void>;
  getFollowedEntities(): Promise<string[]>;
  setPreferredLanguage(language: string | null): Promise<void>;
  stopActiveCastingSession(): Promise<void>;
  showSearchScreen(options: object): Promise<void>;
  stopActivePiPSession(): Promise<void>;
  isPiPActive(): Promise<boolean>;
}

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<void> {
    this.setEntryPointDelegate(options.playerEntryPointDelegate)
    this.setGlobalDelegate(options.globalDelegate)

    return BlazeSDKNativeModule.init(options).catch(mapToBlazeErrorOrRethrow);
  }

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

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

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

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

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

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

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

  playVideo(options: PlayVideoOptions): Promise<void> {
    return BlazeSDKNativeModule.playVideo(options).catch(mapToBlazeErrorOrRethrow);
  }

  prepareVideos(options: PrepareVideosOptions): Promise<void> {
    return BlazeSDKNativeModule.prepareVideos(options).catch(mapToBlazeErrorOrRethrow);
  }

  playVideos(options: PlayVideosOptions): Promise<void> {
    return BlazeSDKNativeModule.playVideos(options).catch(mapToBlazeErrorOrRethrow);
  }

  appendMomentsToPlayer(options: AppendMomentsToPlayerOptions): Promise<void> {
    return BlazeSDKNativeModule.appendMomentsToPlayer(options).catch(mapToBlazeErrorOrRethrow);
  }

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

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

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

  setDisableAnalytics(disableAnalytics: boolean): Promise<void> {
    return BlazeSDKNativeModule.setDisableAnalytics(disableAnalytics);
  }

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

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

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

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

  setDefaultVideosPlaybackConfiguration(config: BlazeVideosPlaybackConfiguration): Promise<void> {
    return BlazeSDKNativeModule.setDefaultVideosPlaybackConfiguration(config).catch(mapToBlazeErrorOrRethrow);
  }

  getDefaultVideosPlaybackConfiguration(): Promise<BlazeVideosPlaybackConfiguration> {
    return BlazeSDKNativeModule.getDefaultVideosPlaybackConfiguration().catch(mapToBlazeErrorOrRethrow);
  }

  setDefaultMomentsPlaybackConfiguration(config: BlazeMomentsPlaybackConfiguration): Promise<void> {
    return BlazeSDKNativeModule.setDefaultMomentsPlaybackConfiguration(config).catch(mapToBlazeErrorOrRethrow);
  }

  getDefaultMomentsPlaybackConfiguration(): Promise<BlazeMomentsPlaybackConfiguration> {
    return BlazeSDKNativeModule.getDefaultMomentsPlaybackConfiguration().catch(mapToBlazeErrorOrRethrow);
  }

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

  handleNotificationPayload(payload: object): Promise<void> {
    return BlazeSDKNativeModule.handleNotificationPayload(payload).catch(mapToBlazeErrorOrRethrow);
  }
  
  setFollowEntitiesDelegate(delegate?: BlazeFollowEntitiesDelegate | null): void {
    BlazeFollowEntitiesDelegateHelper.registerFollowEntitiesDelegate(delegate)
  }

  setFollowedEntities(entityIds: string[]): Promise<void> {
    return BlazeSDKNativeModule.setFollowedEntities(entityIds).catch(mapToBlazeErrorOrRethrow);
  }

  insertFollowedEntities(entityIds: string[]): Promise<void> {
    return BlazeSDKNativeModule.insertFollowedEntities(entityIds).catch(mapToBlazeErrorOrRethrow);
  }

  removeFollowedEntities(entityIds: string[]): Promise<void> {
    return BlazeSDKNativeModule.removeFollowedEntities(entityIds).catch(mapToBlazeErrorOrRethrow);
  }

  getFollowedEntities(): Promise<string[]> {
    return BlazeSDKNativeModule.getFollowedEntities().catch(mapToBlazeErrorOrRethrow);
  }

  setPreferredLanguage(language: string | null): Promise<void> {
    return BlazeSDKNativeModule.setPreferredLanguage(language);
  }

  showSearchScreen(options?: BlazeSearchScreenOptions): Promise<void> {
    return BlazeSDKNativeModule.showSearchScreen(options ?? {}).catch(mapToBlazeErrorOrRethrow);
  }

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

  setCastingDelegate(delegate?: BlazeCastingDelegate | null): void {
    BlazeCastingDelegateHelper.registerCastingDelegate(delegate);
  }

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

  isPiPActive(): Promise<boolean> {
    return BlazeSDKNativeModule.isPiPActive();
  }

  setPipDelegate(delegate?: BlazePipDelegate | null): void {
    BlazePipDelegateHelper.registerPipDelegate(delegate);
  }

  // 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;
