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

const LINKING_ERROR =
  `The package 'react-native-meta-plugin-module' 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 Go\n';

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

export const webIdEventEmitter = new NativeEventEmitter(WebIdMetaPlugin);

export function createMetaPlugin(
  uri: string,
  username: string,
  apiKey: string,
  certsBase64: string[],
  plugins: string[]
): Promise<string> {
  return WebIdMetaPlugin.createMetaPlugin(
    uri,
    username,
    apiKey,
    certsBase64,
    plugins
  );
}

export function verifyActionId(actionId: string): Promise<VerifyActionResult> {
  return new Promise(async (resolve, reject) => {
    try {
      const json = await WebIdMetaPlugin.verifyActionId(actionId);
      let verifyActionResult = parseJSONToType<VerifyActionResult>(json);
      if (verifyActionId != undefined) {
        resolve(verifyActionResult!);
      } else {
        reject('JSON parsing failed');
      }
    } catch (error) {
      reject(error);
    }
  });
}

export function start(
  iOSLightTheme: iOSTheme,
  iOSDarkTheme: iOSTheme
): Promise<string> {
  if (Platform.OS === 'ios') {
    return WebIdMetaPlugin.start(
      JSON.stringify(iOSLightTheme),
      JSON.stringify(iOSDarkTheme)
    );
  } else {
    return WebIdMetaPlugin.start();
  }
}

export function parseJSONToType<T>(json: string): T | undefined {
  try {
    return JSON.parse(json) as T;
  } catch {
    return undefined;
  }
}

export type iOSTheme = {
  Background?: string;
  PrimaryColor?: string;
  AccentColor?: string;
  WarningColor?: string;
  ErrorColor?: string;
  TextColor?: string;
  SuccessColor?: string;
  FontFamily?: string;

  primaryButtonColor?: string;
  primaryButtonBackgroundColor?: string;
  primaryButtonDisabledColor?: string;
  primaryButtonDisabledBackgroundColor?: string;
  primaryButtonFont?: string;
  primaryButtonTextCase?: string; // "upper" or "normal"
  primaryButtonCornerRadius?: number;
  primaryButtonHeight?: number;
  primaryButtonBorderWidth?: number;
  primaryButtonBorderColor?: string;

  secondaryButtonColor?: string;
  secondaryButtonBackgroundColor?: string;
  secondaryButtonDisabledColor?: string;
  secondaryButtonDisabledBackgroundColor?: string;
  secondaryButtonFont?: string;
  secondaryButtonTextCase?: string; // "upper" or "normal"
  secondaryButtonCornerRadius?: number;
  secondaryButtonHeight?: number;
  secondaryButtonBorderWidth?: number;
  secondaryButtonBorderColor?: string;

  tertiaryButtonColor?: string;
  tertiaryButtonBackgroundColor?: string;
  tertiaryButtonDisabledColor?: string;
  tertiaryButtonDisabledBackgroundColor?: string;
  tertiaryButtonFont?: string;
  tertiaryButtonTextCase?: string; // "upper" or "normal"
  tertiaryButtonCornerRadius?: number;
  tertiaryButtonHeight?: number;
  tertiaryButtonBorderWidth?: number;
  tertiaryButtonBorderColor?: string;

  photoButtonColor?: string;
  photoButtonBackgroundColor?: string;
  photoButtonDisabledColor?: string;
  photoButtonDisabledBackgroundColor?: string;

  hangupButtonColor?: string;
  hangupButtonBackgroundColor?: string;

  photoUserInfoColor?: string;
  photoUserInfoBackgroundColor?: string;
  photoCanvasColor?: string;

  signingToolbarBackgroundColor?: string;
  signingToolbarColor?: string;

  checkboxColor?: string;
  checkboxTextColor?: string;
  checkboxFillColor?: string;

  inputColor?: string;
  inputPlaceHolderColor?: string;
  inputBackgroundColor?: string;

  linkColor?: string;

  infoMessageBoxColor?: string;
  infoMessageBoxBackgroundColor?: string;

  warningMessageBoxColor?: string;
  warningMessageBoxBackgroundColor?: string;

  errorMessageBoxColor?: string;
  errorMessageBoxBackgroundColor?: string;

  bottomSheetBackgroundColor?: string;

  alertColor?: string;
  alertBackgroundColor?: string;
  alertButtonTextColor?: string;

  downloadSymbolColor?: string;

  headlineColor?: string;
  headlineFont?: string;
  headlineTextCase?: string; // "upper" or "normal"
};

export type VerifyActionResult = {
  userAction: UserAction;
};

export type UserAction = {
  actionId: string;
  customerNo: string;
  envURI: string;
  expirationDate: string;
  userActionType: UserActionType;
  userActionStatus: UserActionStatus;
};

export type UserActionStatus = {
  identState: UserActionIdentState;
  qesState: UserActionQesState;
  identTanState: UserActionTanState;
  timestamp: string;
  mismatch: boolean;
  webIdEntryUrl: string;
  videoCallInfo: VideoCallInfo;
};

export type VideoCallInfo = {
  nextCallcenterOpening: string;
  expirationDate: string;
};

export type ProductPluginResultFailed = {
  genericResultCode: string;
  genericResultMessage: string;
  faultOriginator: string;
  additionalResult: string;
  specificResultCode: string;
};

export type ProductPluginResultSuccess = {
  processFinished: boolean;
  additionalResult: string;
};

// EVENTS

export enum WebIdSdkEvent {
  finishedFailed = 'WebIdSdkEvent.finishedFailed',
  finishedSuccess = 'WebIdSdkEvent.finishedSuccess',
}

// SUCCESS

export enum WebIdSdkSuccess {
  CoreSDKCreated = 'WebIdSdkSuccess.CoreSDKCreated',
  PluginStarted = 'WebIdSdkSuccess.PluginStarted',
}

// ERRORS

export enum WebIdSdkGenericError {
  UnknownException = 'WebIdSdkGenericError.UnknownException',
  SDKNotInitialized = 'WebIdSdkGenericError.SDKNotInitialized',
  MissingVerifyActionIdResult = 'WebIdSdkGenericError.MissingVerifyActionIdResult',
}

export enum WebIdSdkEnvironmentError {
  /** Signals that the required pinning certificate(s) for the URL is missing!*/
  PinningCertificateMissing = 'WebIdSdkEnvironmentError.PinningCertificateMissing',
  /** Thrown to indicate that environment URL is invalid!*/
  InvalidUrl = 'WebIdSdkEnvironmentError.InvalidUrl',
  /** Unknown Error*/
  Unknown = 'WebIdSdkEnvironmentError.Unknown',
}

export enum WebIdMobileAppSdkError {
  /** Thrown to indicate that an IllegalArgument Exception happened */
  IllegalArgument = 'WebIdMobileAppSdkError.IllegalArgument',
  /** Thrown to indicate that the Api conversion fails. Is a fatal or non-recoverable error in the SDK itself.*/
  ApiToDomainCoversionFailed = 'WebIdMobileAppSdkError.ApiToDomainCoversionFailed',
  /** Signals that the SDK operation or method usage is denied!*/
  AccessDenied = 'WebIdMobileAppSdkError.AccessDenied',
  /** Signals that the SDK authentification failed!*/
  AuthenticationFailed = 'WebIdMobileAppSdkError.AuthenticationFailed',
  /** Signals that there is an unexpected network error, e.g. no internet connection, low bandwith, etc.*/
  NSURLErrorDomain = 'WebIdMobileAppSdkError.NSURLErrorDomain',
  /** Unexpected HTTP response from SDK API. Is a fatal or non-recoverable error in the SDK itself.*/
  HttpError = 'WebIdMobileAppSdkError.HttpError',
  /** An uncategorized fatal or non-recoverable error in the SDK itself.*/
  FatalError = 'WebIdMobileAppSdkError.FatalError',
  /** Signals that the certificate being used cannot be used for a valid certificate pinning with the specified environment.*/
  CertificatePinningFailed = 'WebIdMobileAppSdkError.CertificatePinningFailed',
  /** The SDK cannot connect to the internet.*/
  NetworkConnectionFailed = 'WebIdMobileAppSdkError.NetworkConnectionFailed',
  /** Thrown to indicate that the service is currently unavailable.*/
  ServiceUnavailableException = 'WebIdMobileAppSdkError.ServiceUnavailableException',
  /** Unknown Error*/
  Unknown = 'WebIdMobileAppSdkError.Unknown',
}

export enum UserActionError {
  /** Means that the user has to perform authada eId.*/
  EidAuthadaRequired = 'UserActionError.EidAuthadaRequired',
  /** Means that that the action has an ident state that can not be used in app SDK.*/
  IdentNotSupported = 'UserActionError.IdentNotSupported',
  /** Signals that user-action is expired, canceled or finished.*/
  IllegalState = 'UserActionError.IllegalState',
  /** Thrown to indicate that no user-action was found for given action-id. */
  NotFound = 'UserActionError.NotFound',
  /** Means that the session token has expired. verifyActionId() needs to be called to get a new session*/
  SessionExpired = 'UserActionError.SessionExpired',
  /** TODO android only*/
  LegalTermsConfirmationMissing = 'UserActionError.LegalTermsConfirmationMissing',
  /** Unknown Error*/
  Unknown = 'UserActionError.Unknown',
}

export enum ProductPluginErrors {
  /** Unknown reason. The plugin execution failed unexpectedly and unpredictably. An additional object will contain the reason. */
  unknown = 'EProductPluginErrors.unknown',
  /** The user has aborted or canceled the plugin. */
  canceledByUser = 'EProductPluginErrors.canceledByUser',
  /** The state of the user-action prohibits plugin execution. Please call verifyActionId() to check the user-action state. */
  notAllowed = 'EProductPluginErrors.notAllowed',
  /** A technical error is a recoverable error like a network error. A restart of the plugin is allowed. Please call verifyActionId() to check the user-action state and that you can connect the SDK server */
  technical = 'EProductPluginErrors.technical',
  /** Fatal error in API usage, which means that the plugin cannot be used. You must stop to use the plugin and investigate the error. Ensure that you are using a supported plugin and SDK there are no dependencies issue etc.*/
  api = 'EProductPluginErrors.api',
  /** One of the services needed by this plugin is currently unavailable. */
  serviceUnavailable = 'EProductPluginErrors.serviceUnavailable',
  /** The session was expired. Please call verifyActionId() to check the user-action state. */
  sessionExpired = 'EProductPluginErrors.sessionExpired',
  /** The SDK user - specified by its credentials - cannot be authenticated. */
  notAuthenticated = 'EProductPluginErrors.notAuthenticated',
  /** The SDK user - specified by its credentials - is not allowed to call the plugin specific methods.*/
  notAuthorized = 'EProductPluginErrors.notAuthorized',
  /** The user-action was not found or isn't usable anymore. Please call verifyActionId() to check the user-action state.*/
  userActionNotFound = 'EProductPluginErrors.userActionNotFound',
  /** The used module plugin was interrupted during its execution.*/
  pluginInterrupted = 'EProductPluginErrors.pluginInterrupted',
  /** The used module plugin failed to start.*/
  pluginStartFailed = 'EProductPluginErrors.pluginStartFailed',
  /** Product selection must be performed before plugin can be used. Open UserActionStatus.webIdEntryUrl in browser to let end-user select a product.*/
  productSelectionRequired = 'EProductPluginErrors.productSelectionRequired',
  /** The user has already switched to another WebID identification product. Check the field additionalResult, it tells which is the current selected WebID product.*/
  switchedToProduct = 'EProductPluginErrors.switchedToProduct',
}

// ENUMS
// TODO: add documentation
export enum UserActionType {
  age16 = 'age16',
  age18 = 'age18',
  age21 = 'age21',
  cert = 'cert',
  doc = 'doc',
  pass = 'pass',
  sig = 'sig',
  unknown = 'unknown',
}

export enum UserActionTanState {
  /** Means that a TAN was generated but is expired.*/
  expired = 'expired',
  /** Means that a TAN was generated but is still unverified.*/
  setAndUnverified = 'setAndUnverified',
  /** Indicates that a TAN was generated and verified.*/
  setAndVerified = 'setAndVerified',
  /** Indicates that no TAN was generated.*/
  unset = 'unset',
  /** Unknown state*/
  unknown = 'unknown',
}

export enum UserActionIdentState {
  /** Means that the agent is currently working on the user-action. */
  agentEditing = 'agentEditing',
  /** Means that the end-user has to do the AutoIdent - scanning its id-document, etc. by himself - before the process can be continued.*/
  autoIdentPending = 'autoIdentPending',
  /** Means that the customer has manually terminated the user-action before the identification-phase was completed.*/
  canceled = 'canceled',
  /** Means that the end-user has to do the eCheck - reusing its MyWebID by using a TAN-based process.*/
  echeckPending = 'echeckPending',
  /** Means that the end-user has to do an eid-ident before the process can be continued.*/
  eidPendingAuthada = 'eidPendingAuthada',
  /** Means that the identification-phase is expired and not completed.*/
  expired = 'expired',
  /** Means that the end-user has to confirm terms and conditions of WebID. The end-user needs to confirm them before the process can be continued.*/
  legalTermsConfirmationRequired = 'legalTermsConfirmationRequired',
  /** Means that that the action has an ident state that can not be used in app SDK. Please redirect the end-user via a web-browser to UserActionStatus.webIdEntryUrl, so that the end-user can finish the identification on WebID's website.*/
  notSupported = 'notSupported',
  /** Means that the end-user has to do the preCheck - scanning its id-document, etc. by himself - before the process can be continued.*/
  precheckPending = 'precheckPending',
  /** Means that the identification-phase was completed successfully.*/
  successful = 'successful',
  /** Means that the agent has confirmed the end-user but the end-user did not finalize the process with a valid TAN yet.*/
  tanPending = 'tanPending',
  /** Means that the user action was created and it is possible to finish the ident by TrueID if an appropriate reference user action is found.*/
  trueidPending = 'trueidPending',
  /** Means that the identification-phase was completed unsuccessfully.*/
  unsuccessful = 'unsuccessful',
  /** Means that the end-user needs to make a video-call with a callcenter-agent.*/
  videocallPending = 'videocallPending',
  /** Unknown state*/
  unknown = 'unknown',
}

export enum UserActionQesState {
  /**  Means that the customer (not the end-user) has manually terminated the user-action before the qes-phase was completed. */
  canceled = 'canceled',
  /** Indicates for `sig` user-actions that the document(s) to be signed is/are missing. `cert` user-actions will never be in this state. */
  documentMissing = 'documentMissing',
  /** Indicates that the qes-phase is expired and not completed. */
  expired = 'expired',
  /** Means that the user-action do not have a qes phase. */
  notApplicable = 'notApplicable',
  /** Means that the qes phase is pending. For `sig` user-action this means that the document(s) to be signed is/are available. */
  pending = 'pending',
  /** Indicates that the qes-phase was completed successfully. */
  successful = 'successful',
  /** Unknown state */
  unknown = 'unknown',
}
