/**
 * This exposes the native AppConsent module as a JS module.
 *
 * @packageDocumentation
 */

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

const LINKING_ERROR =
  `The package 'appconsent-clear-reactnative' doesn't seem to be linked. Make sure: \n\n` +
  Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
  '- You rebuilt the app after installing the package\n' +
  '- You are not using Expo managed workflow\n';

const appConsent = NativeModules.AppconsentClearReactnative
  ? NativeModules.AppconsentClearReactnative
  : new Proxy(
      {},
      {
        get() {
          throw new Error(LINKING_ERROR);
        },
      }
    );

/**
 * This is the main entry point to interact with AppConsent API.
 *
 * @example
 * import AppConsent from 'appconsent-clear-reactnative';
 */
export default appConsent as AppConsent;

/**
 * This is the main interface to interact with AppConsent API, it exposes typescript declarations to access Native libraries features.
 *
 * @example
 * import AppConsent from 'appconsent-clear-reactnative';
 */
export interface AppConsent {
  /**
   * This initializes AppConsent.
   * @remarks
   * This method is <b>ASYNCHRONOUS!</b><br />
   * You must call the method with <b>await</b>.<br />
   * To ensure proper operation, put the call to this method in a <b>try/catch</b>.<br />
   * This method doesn't have a <b>Promise<T></b> signature<br />
   * So it can't be used as such (<i>promiseMethod.then().catch()</i>)
   *
   * @param   appKey - Your AppKey
   * @param   forceApplyGDPR - force behavior as in a GDPR subject country<br />
   *        false, let the CMP check whether the user is subject to the GDPR of its region.<br />
   *        true, it considers the user to be subject to GDPR
   * @param   forceATT - force ATT handling (iOS only)
   *
   * @category General Use
   */
  configureWith(
    appKey: string,
    forceApplyGDPR: boolean,
    forceATT: boolean
  ): void;

  /**
   * To display AppConsent by modal on top of your view.
   *
   * @remarks
   * To be notified when the user has entered his consent, don't forget to register with the callback.
   *
   * @see {@link AppConsent.consentGiven}
   * @param force - false, displays the introduction screen as a modal only if necessary (subject to GDPR, consent not entered, consent period exceeded, etc.).<br />
   *                true, forces the display of the configuration screen (allowing users to enter/modify their consent)
   *
   * @category General Use
   */
  present(force: boolean): void;

  /**
   * Check if consent must be updated (new gvl version, new consentables…)
   *
   * @example
   * AppConsent.checkForUpdate()
   *   .then((isNeedToUpdate: boolean) => {
   *      console.log('isNeedToUpdate => ' + isNeedToUpdate);
   *      // CMP needs to be displayed again to your users
   *      AppConsent.present(false);
   *   }).catch((err: any) => console.log(err));
   *
   * @category Advanced Use
   */
  checkForUpdate(): Promise<boolean>;

  /**
   * Check if device is GDPR country.
   *
   * @returns true if the device is in a country subject to GDPR, false otherwise.
   *
   * @category Advanced Use
   */
  isGDPRCountry(): Promise<boolean>;

  /**
   * Check if user gave consent
   *
   * @example
   * AppConsent.consentAlreadyGiven()
   *   .then( success =>
   *     console.log("🔥 consentAlreadyGiven => " + success)
   *    )
   *
   * @returns true if consent is given, false otherwise.
   *
   * @category Advanced Use
   */
  consentAlreadyGiven(): Promise<boolean>;

  /**
   * Callback when user give its consent.
   *
   * @example
   * AppConsent.consentGiven()
   *   .then((success: boolean) => console.log('consentGiven => ' + success))
   *   .catch((err: any) => console.log(err));
   *
   * @returns true if user saved their consent preferences.
   *
   * @category Advanced Use
   */
  consentGiven(): Promise<boolean>;

  /**
   * Clear consents on NSUserDefaults, DefaultSharedPreference, but not on server.
   *
   * @category Advanced Use
   */
  clearConsent(): void;

  /**
   *
   * @param extraId
   * @returns true if consentable with extraId is allowed, false otherwise.
   *
   * @category Advanced Use
   */
  extraConsentableAllowed(extraId: string): Promise<boolean>;

  /**
   *
   * @param extraId
   * @returns true if consentable with extraId is allowed, false otherwise.
   *
   * @category Advanced Use
   */
  extraVendorAllowed(extraId: string): Promise<boolean>;

  /**
   * Check if floating purpose is allowed.
   *
   * @remarks
   * NOTE:
   * You need to call checkForUpdate method before calling this method to stay up to date.
   *
   * @see {@link AppConsent.checkForUpdate}
   *
   * @param extraId
   *
   * @category Advanced Use
   */
  extraFloatingPurposeAllowed(extraId: string): Promise<boolean>;

  /**
   * Set consentables status with an iabId.
   *
   * @remarks
   * CAUTION:
   * Key must be an iabId, not an objectId.
   * Value must be an Integer: 0 (pending), 1 (allowed), -1 (denied)
   *
   * NOTE:
   * This method doesn’t send request to the server.
   *
   * @param values
   *
   * @category Advanced Use
   */
  setConsentableConsent(values: {}): Promise<boolean>;

  /**
   * Set consentables status with extraId.
   *
   * @remarks
   * CAUTION:
   * Key must be an extraId, not an objectId.
   * Value must be an Integer: 0 (pending), 1 (allowed), -1 (denied)
   *
   * NOTE:
   * This method doesn’t send request to the server.
   *
   * @param values
   *
   * @category Advanced Use
   */
  setExtraConsentableConsent(values: {}): Promise<boolean>;

  // EXTERNAL IDS

  /**
   * Retrieve external ids from cache.
   *
   * @remarks
   * NOTE: this only returns the external ids present in the local storage, without checking them out from the backend, external ids are only synchronized after a call to present() or checkForUpdate().
   *
   * @returns String representation of all the external Ids in User Preferences
   *          in the following form :
   *          [:] for an empty value
   *          ["key1": "value1", "key2": "value2"] for a key/value data set
   *
   * @category External Ids
   */
  getExternalIds(): Promise<string>;

  /**
   * Store a set of external id allong the consent, the ids are synchornised with the consents upon calling present() or checkForUpdate(), so this should be called before calling present() or checkForUpdate().
   *
   * @remarks
   * NOTE: this overrides previously set ExternalIds in the local storage, but the change won't be synchronized with our backend untill a call to present() or checkForUpdate() is made.
   *
   * @returns true
   *
   * @param ids
   *
   * @category External Ids
   */
  setExternalIds(ids: {}): Promise<boolean>;

  /**
   * Send the external ids you have stored with setExternalIds to the server.
   *
   * @remarks
   * NOTE: the external ids are also saved automatically when calling present(), whether or not the notice is presented, or when calling checkForUpdate().
   * Saving ExternalIds with this method doesn't work unless there has been a previous consent registered with present(), so in most cases it is preferable to simply rely on present() or checkForUpdate() to save the ExternalIds.
   *
   * @example
   * AppConsent.setExternalIds({ myCustomID: 'xyz' })
   *   .then((success: boolean) =>
   *     console.log('🔥 setExternalIds => ' + success)
   *   )
   *   .catch((err: any) => console.log(err))
   *
   * @returns true
   *
   * @category External Ids
   */
  saveExternalIds(): Promise<boolean>;

  // IOS ONLY

  /**
   * @remarks
   * This is only supported on iOS targets.
   *
   * @returns forceATT
   *
   * @category iOS App Tracking Transparency
   */
  getForceATT(): Promise<boolean>;

  /**
   * Check if your device supports App Tracking Transparency.
   *
   * @remarks
   * This is only supported on iOS targets.
   *
   * @returns true if available, false otherwise.
   *
   * @category iOS App Tracking Transparency
   */
  appTrackingIsAvailable(): Promise<boolean>;

  /**
   * Check if App Tracking Transparency has been given.
   *
   * @remarks
   * This is only supported on iOS targets.
   *
   * @returns 0 (not given), 1 (given), 2 (not supported)
   *
   * @category iOS App Tracking Transparency
   */
  appTrackingAuthorizationGiven(): Promise<number>;

  /**
   * Check status of App Tracking Transparency.
   *
   * @remarks
   * This is only supported on iOS targets.
   *
   * @returns 0 (failure), 1 (success), 2 (not supported)
   *
   * @category iOS App Tracking Transparency
   */
  appTrackingAuthorizationStatus(): Promise<number>;

  // DEPRECATED

  /**
   * @returns true if consentable with objectId is allowed, false otherwise.
   *
   * @deprecated
   * @category Advanced Use
   */
  consentableAllowedByObjectId(id: string): Promise<boolean>;

  /**
   * @param iabId
   * @param type - 0: purpose, 1: feature, 2: specialFeature, 3: specialPurpose
   * @returns true if consentable with iabId is allowed, false otherwise.
   *
   * @deprecated
   * @category Advanced Use
   */
  consentableAllowedByIABId(iabId: string, type: number): Promise<boolean>;

  /**
   * @returns true if consentable with iabId is allowed, false otherwise.
   *
   * @deprecated
   * @category Advanced Use
   */
  vendorAllowedByIABId(iabId: string): Promise<boolean>;

  /**
   * @returns true if consentable with iabId is allowed, false otherwise.
   *
   * @deprecated
   * @category Advanced Use
   */
  stackAllowedByIABId(iabId: string): Promise<boolean>;

  /**
   * @returns the configured AppKey.
   *
   * @deprecated
   * @category Advanced Use
   */
  getAppKey(): Promise<string>;

  /**
   * @returns forceApplyGDPR
   *
   * @deprecated
   * @category Advanced Use
   */
  getForceApplyGDPR(): Promise<boolean>;

  /**
   *
   * @deprecated
   * @category Advanced Use
   */
  getName(): string;

  /**
   *
   * @deprecated
   * @category Advanced Use
   */
  getConstants(): any;
}
