import {
  NativeModules,
  Platform,
  NativeEventEmitter,
  DeviceEventEmitter,
} from 'react-native';

const OneTrust = NativeModules.OneTrust;
const iOSBroadcast = NativeModules.OneTrustConsentBroadcast;

// Create event emitter for OneTrust events
const oneTrustEventEmitter = Platform.OS === 'ios' 
  ? new NativeEventEmitter(iOSBroadcast)
  : new NativeEventEmitter(OneTrust);

export default class OTPublishersNativeSDK{

  /**
   * @param {string} storageLocation  Usually cdn.cookielaw.org - this value comes from your Admin Console
   * @param {string} domainIdentifier Also called App ID, this comes from your Admin Console and is a GUID. It may have -test on the end if it's the test version.
   * @param {string} languageCode  The 2-digit ISO Language code
   * @param {object} params  Currently accepts countryCode, regionCode, and profileSyncParams
   * @param {string} params.countryCode  Two-digit ISO country code for the user
   * @param {string} params.regionCode  Two-digit ISO region code for the user
   * @param {[key:string]:string} accepts enableDarkMode for Android.
   * @param {string} params.androidUXParams Stringified JSON object containing the UXParams object to override 
   * @param {[key:string]:string} params.profileSyncParams JSON Object containing 'identifier' and 'profileSyncAuth' values
   * @param {boolean} autoShowBanner Should the banner be shown automatically when download is complete? Takes the shouldShowBanner logic into account
   * @returns {Promise} Promise object contains status of download, error (if download fails), and responseString, which is a JSON string representing the data downloaded from the OneTrust server.
   */
   static async startSDK(storageLocation:string, domainIdentifier:string, languageCode:string, params:{[key:string]:string|{[key:string]:string}},autoShowBanner:boolean):Promise<object>{
    return OneTrust.startSDK(storageLocation,domainIdentifier,languageCode, params, autoShowBanner)
  }

  /**
   * Force load the banner
   * @param {[key:string]:string} Currently accepts enableDarkMode for Android.
   */
  static showBannerUI(params:{[key:string]:string|{[key:string]:string}}):void {
    if (Platform.OS === 'android') {
      OneTrust.showBannerUI(params);
    } else {
      OneTrust.showBannerUI();
    }
  } 

  /**
   * Load the OneTrust Preference Center, usually placed behind a button
   * @param {[key:string]:string} Currently accepts enableDarkMode for Android.
   */
  static showPreferenceCenterUI(params:{[key:string]:string|{[key:string]:string}}):void {
    if (Platform.OS === 'android') {
      OneTrust.showPreferenceCenterUI(params);
    } else {
      OneTrust.showPreferenceCenterUI();
    }
  }

  /**
   * Determine whether or not the banner should be shown
   * @returns {promise} Promise object is a boolean indicating whether or not the banner should be shown
   */
  static shouldShowBanner():Promise<boolean> {
    return OneTrust.shouldShowBanner();
  }

  /**
   * Get the current consent status for the given category
   * @param {string} categoryId String of the category ID for which you'd like to retrieve consent
   * @returns {number} 1 = consent given; 0 = consent not given; -1 = category does not exist or SDK not initialized
   */
  static getConsentStatusForCategory(categoryId:string):Promise<number> {
    return OneTrust.getConsentStatusForCategory(categoryId);
  }

  /**
   * Get the current consent status for the given category
   * @param {string} SDKId String of the SDK GUID for which you'd like to retrieve consent
   * @returns {number} 1 = consent given; 0 = consent not given; -1 = GUID does not exist or SDK not initialized
   */
   static getConsentStatusForSDKId(SDKId:string):Promise<number> {
    return OneTrust.getConsentStatusForSDKId(SDKId);
  }

  /**
   * Sets the values that can be observed. Required for iOS.
   * @param {array} categories Array of strings indicating which categories will have listeners activated in subsequent calls.
   */
  static setBroadcastAllowedValues(categories:string[]) {
    if (Platform.OS !== 'android') {
      iOSBroadcast.setAllowedCategories(categories);
    }
  }

  /**
   * Listen for consent changes to a particular category
   * @param {string} category String of the category ID to listen for
   * @callback 
   * @param {string} category Category that was changed. Matches the category in listener registration
   * @param {number} consent Consent value (1 = consent given, 0 = consent not given)
   * @returns {object} Returns an event emitter that can be unsubscribed from when your component dismounts
   */
  static listenForConsentChanges(category:string, callback = function (category:string,consent:number) {}) {
    if (Platform.OS === 'android') {
      OneTrust.listenForConsentChanges(category);
      return DeviceEventEmitter.addListener(category, (consent:number) =>
        callback(category, consent),
      );
    } else {
      iOSBroadcast.listenForConsentChanges(category);
      const consentListener = new NativeEventEmitter(iOSBroadcast);
      return consentListener.addListener(category, (consent:number) =>
        callback(category, consent),
      );
    }
  }

  /**
   * Stop the platform code from emitting events
   */
  static stopListeningForConsentChanges() {
    if(Platform.OS === 'android'){
      OneTrust.stopListeningForConsentChanges();
    }else{
      iOSBroadcast.stopListeningForConsentChanges();
    }
  }

  /**
   * @returns Promise object is a string of the JS to inject in your webview
   */
  static getOTConsentJSForWebView():Promise<string>{
    return OneTrust.getOTConsentJSForWebView();
  }

  /**
   * @returns Promise object is a dictionay of the key values
   */
  static async getDomainInfo():Promise<JSON>{
    let domainInfo = await OneTrust.getDomainInfo()
    return JSON.parse(domainInfo);
  }

    /**
   * @returns Promise object is a dictionay of the key values
   */
    static async getOTGoogleConsentModeData():Promise<JSON>{
      let googleConsentModeData = await OneTrust.getOTGoogleConsentModeData()
      return JSON.parse(googleConsentModeData);
    }

  /**
   * Fetches Banner CMP API data
   * @returns Promise containing the banner CMP API data as a JSON string
   */
  static fetchBannerCmpApiData(): Promise<string> {
    return OneTrust.fetchBannerCmpApiData();
  }

  /**
   * Fetches Preferences CMP API data
   * @returns Promise containing the preferences CMP API data as a JSON string
   */
  static fetchPreferencesCmpApiData(): Promise<string> {
    return OneTrust.fetchPreferencesCmpApiData();
  }

  /**
   * Fetches Vendors CMP API data
   * @returns Promise containing the vendors CMP API data as a JSON string
   */
  static fetchVendorsCmpApiData(): Promise<string> {
    return OneTrust.fetchVendorsCmpApiData();
  }  

  /**
   * [iOS Only] Show consent for device-level permission.
   * Promise resolved immediately on Android
   */
  static showConsentUI(permissionType:OTDevicePermission):Promise<void>{
    if(Platform.OS == 'ios'){
      return OneTrust.showConsentUI(permissionType)
    }else{
      return Promise.resolve()
    }
  }

  /**
   * [iOS Only] Get status of iOS App Tracking Transparency
   * Only valid for iOS 14+
   * @returns String: 'authorized', 'denied','notDetermined',or 'restricted'. Returns "n/a" on Android.
   */
  static getATTStatus():Promise<String>{
    if(Platform.OS == 'ios'){
      return OneTrust.getATTStatus()
    }
    else{
      return Promise.resolve("n/a")
    } 
  }
 /**
  * Display the Universal Consent preference center
  */
  static showConsentPurposesUI():void{
    OneTrust.showConsentPurposesUI()
  }

  /**
   * Method to clear OT SDK data.
   */
  static clearOTSDKData():void{
    OneTrust.clearOTSDKData()
  }

  /**
   * Retrieves the status of the specified universal consent purpose
   * @param {string} purposeId The id of the purpose to query against
   * @returns {number} 1 if consent is granted, 0 if consent is not granted
   */
  static getUCPurposeConsent(purposeId:string):Promise<Boolean>{
    return OneTrust.getUCPurposeConsent(purposeId)
  }

  /**
   * Retrieves an array of key/value pairs representing the custom preferences' statuses
   * @param {string} customPreferenceOptionId The id of the option selected
   * @param {string} customPreferenceId The id of the custom preferences
   * @param {string} purposeId The id of the purpose under which the custom preference is nested
   * @returns {number} 1 if consent is granted, 0 if consent is not granted
   */
  static getUCCustomPreferenceConsent(customPreferenceOptionId:string, customPreferenceId:string, purposeId:string):Promise<number>{
    return OneTrust.getUCCustomPreferenceConsent(customPreferenceOptionId,customPreferenceId,purposeId)
  }
  
  /**
   * Retrieves the consent status of the specified topic
   * @param {string} topicOption the GUID of the topic option to retrieve
   * @param {string} purposeId the GUID of the purpose under which the topic is nested
   * @returns {number} 1 if consent is granted, 0 if consent is not granted
   */
  static getUCTopicConsent(topicOption:string, purposeId:string):Promise<number>{
    return OneTrust.getUCTopicConsent(topicOption, purposeId)
  }

  /**
   * Updates the top-level purpose consent for UC purposes. Must call saveUCConsent() to commit changes.
   * @param {string} purposeId the GUID of the purpose
   * @param {boolean} consent the value of whether or not consent has been granted
   */

  static updateUCPurposeConsent(purposeId:string, consent:boolean):void{
    OneTrust.updateUCPurposeConsent(purposeId, consent)
  }
  /**
   * Updates a custom preference nested under a purpose
   * @param {string} customPreferenceOptionId GUID representing the selected option
   * @param {string} customPreferenceId GUID representing the custom preference group
   * @param {string} purposeId the GUID of the purpose under which the custom preferences are nested
   * @param {boolean} consent the value of whether or not consent has been granted
   */
  static updateUCCustomPreferenceConsent(customPreferenceOptionId:string, customPreferenceId:string, purposeId:string, consent:boolean):void{
    OneTrust.updateUCCustomPreferenceConsent(customPreferenceOptionId, customPreferenceId, purposeId, consent)
  }

  /**
   * Update a topic nested under a purpose
   * @param {string} topicOptionId GUID represending the selected topic
   * @param {string} purposeId GUID representing the purpose under which the topic is present
   * @param {boolean} consent the value of whether or not consent has been granted
   */
  static updateUCTopicConsent(topicOptionId:string, purposeId:string, consent:boolean):void{
    OneTrust.updateUCTopicConsent(topicOptionId, purposeId, consent)
  }

  /**
   * Commits the consent changes and submits receipt
   */
  static saveUCConsent():void{
    OneTrust.saveUCConsent();
  }

  /**
   * Gets the current DSID for the active consent profile
   */
  static getCurrentActiveProfile():Promise<string>{
    return OneTrust.getCurrentActiveProfile();
  }

  /**
 * Renames a profile from one identifier to another
 * @param fromIdentifier The current identifier (can be null for anonymous profiles)
 * @param toIdentifier The new identifier to rename to
 * @returns Promise that resolves to true on success
 */
  static renameProfile(fromIdentifier: string | null, toIdentifier: string): Promise<boolean> {
    return OneTrust.renameProfile(fromIdentifier, toIdentifier);
  }

   /**
   * save and log consent for with given interation type.
   */
  static saveConsent(consentType:OTConsentInteraction): Promise<null> {
    return OneTrust.saveConsent(consentType)
  }


   /**
   * The OneTrust SDK supports the ability to customize log types printed to the console.
   * You can control this log level by passing in your preferred minimum log level.
   */
   static enableOTSDKLog(logLevel:OTLoggerConstant): void {
    return OneTrust.enableOTSDKLog(logLevel)
  }

  /**
   * Programmatically sets the consent value of the given category. Nothing is saved to disk until saveConsent is called.
   * @param categoryId the ID (eg C0002) of the category to be updated
   * @param consentValue the value of the consent; true = opt-in, false = opt-out
   */
  static updatePurposeConsent(categoryId:string, consentValue:boolean){
    OneTrust.updatePurposeConsent(categoryId,consentValue)
  }
  /**
   * Discards any changes made to category consent that have not yet been saved to disk.
   * Used when a user abandons the preference center so that the toggles don't show previously abandoned values.
   */
  static resetUpdatedConsent(){
    OneTrust.resetUpdatedConsent
  }

  private static eventListenerInitialized = false;
  private static iosEventListenerInitialized = false;

  /**
   * Initialize event listener for Android
   * This must be called before adding any event listeners on Android
   */
  private static initializeAndroidEventListener() {
    if (!OTPublishersNativeSDK.eventListenerInitialized && Platform.OS === 'android') {
      OneTrust.addEventListener();
      OTPublishersNativeSDK.eventListenerInitialized = true;
    }
  }

  /**
   * Initialize event listener for iOS
   * This must be called before adding any event listeners on iOS
   */
  private static initializeIOSEventListener() {
    if (!OTPublishersNativeSDK.iosEventListenerInitialized && Platform.OS === 'ios') {
      OneTrust.addEventListener();
      OTPublishersNativeSDK.iosEventListenerInitialized = true;
    }
  }

  /**
   * Add a listener for OneTrust events
   * @param eventName The name of the event to listen for
   * @param callback Function to be called when the event occurs
   * @returns Event subscription that can be removed
   * 
   * Note: On first call, this will automatically initialize the native event listener infrastructure.
   * 
   * Available events:
   * - onShowBanner, onHideBanner, onBannerClickedAcceptAll, onBannerClickedRejectAll
   * - onShowPreferenceCenter, onHidePreferenceCenter, onPreferenceCenterAcceptAll, 
   *   onPreferenceCenterRejectAll, onPreferenceCenterConfirmChoices
   * - onShowVendorList, onHideVendorList, onVendorAcceptAll, onVendorRejectAll, onVendorConfirmChoices
   * - onShowSDKList, onHideSDKList
   * - allSDKViewsDismissed
   * - onPreferenceCenterPurposeConsentChanged, onPreferenceCenterPurposeLegitimateInterestChanged
   * - onSDKListSDKConsentChanged
   */
  static addEventListener(eventName: OTEventName, callback: (data?: any) => void) {
    // Initialize native event listener on first call
    if (Platform.OS === 'android') {
      OTPublishersNativeSDK.initializeAndroidEventListener();
      return DeviceEventEmitter.addListener(eventName, callback);
    } else {
      OTPublishersNativeSDK.initializeIOSEventListener();
      const iosEventEmitter = new NativeEventEmitter(OneTrust);
      return iosEventEmitter.addListener(eventName, callback);
    }
  }
}

export enum OTEventName {
  showBanner = 'onShowBanner',
  hideBanner = 'onHideBanner',
  bannerClickedAcceptAll = 'onBannerClickedAcceptAll',
  bannerClickedRejectAll = 'onBannerClickedRejectAll',
  showPreferenceCenter = 'onShowPreferenceCenter',
  hidePreferenceCenter = 'onHidePreferenceCenter',
  preferenceCenterAcceptAll = 'onPreferenceCenterAcceptAll',
  preferenceCenterRejectAll = 'onPreferenceCenterRejectAll',
  preferenceCenterConfirmChoices = 'onPreferenceCenterConfirmChoices',
  showVendorList = 'onShowVendorList',
  hideVendorList = 'onHideVendorList',
  vendorConfirmChoices = 'onVendorConfirmChoices',
  allSDKViewsDismissed = 'allSDKViewsDismissed',
  preferenceCenterPurposeConsentChanged = 'onPreferenceCenterPurposeConsentChanged',
  preferenceCenterPurposeLegitimateInterestChanged = 'onPreferenceCenterPurposeLegitimateInterestChanged'
}

export enum OTDevicePermission{
  /**
   * Select for the IDFA/App Tracking Transparency prompt
   */
  IDFA = 0
}

export enum OTConsentInteraction {
  /**
   *  Choose one of the below for saving and logging consent.
   */
  bannerAllowAll = 1,
  bannerRejectAll = 2,
  bannerClose = 3,
  bannerContinueWithoutAccepting = 4,
  preferenceCenterAllowAll = 5,
  preferenceCenterRejectAll = 6,
  preferenceCenterConfirm = 7,
  preferenceCenterClose = 8
}

export enum OTLoggerConstant {
  /**
   *  Choose one of the below for log level.
   */
  noLogs = 1,
  verbose = 2,
  debug = 3,
  info = 4,
  warning = 5,
  error = 6
}