import { NativeModules, NativeModulesStatic } from "react-native";
import { parseJsonIfNeeded } from "@applicaster/zapp-react-native-utils/functionUtils";
import { DEFAULT_NAMESPACE } from "../consts";

import {
  throwNativeStorageError,
  getItemError,
  setItemError,
  removeItemError,
} from "../errors";

import { Storage, stringifyIfNeeded } from "../Storage";
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";

const storage = "LocalStorage";

function getDefaultLocalStorage<T = NativeLocalStorageI>(): T {
  const { LocalStorage } = NativeModules as NativeModulesStatic & {
    LocalStorage: T;
  };

  return LocalStorage;
}

function getLocalStorageAndroid(): NativeLocalStorageI {
  const NativeLocalStorage =
    getDefaultLocalStorage<NativeAndroidLocalStorageI>();

  return {
    ...NativeLocalStorage,
    getAllItems: NativeLocalStorage?.getNamespace,
  };
}

const LocalStorage = platformSelect({
  android: getLocalStorageAndroid(),
  amazon: getLocalStorageAndroid(),
  android_tv: getLocalStorageAndroid(),
  default: getDefaultLocalStorage(),
});

class LocalStorageModule extends Storage {
  private static _instance: LocalStorageModule;

  static get instance() {
    if (!LocalStorageModule._instance) {
      LocalStorageModule._instance = new LocalStorageModule();
    }

    return LocalStorageModule._instance;
  }

  constructor() {
    super(storage, LocalStorage);

    this.getKeychainItem = this.getKeychainItem.bind(this);
    this.setKeychainItem = this.setKeychainItem.bind(this);
    this.removeKeychainItem = this.removeKeychainItem.bind(this);
  }

  // Secure Storage (LocalStorage only)

  async getKeychainItem(key, namespace = DEFAULT_NAMESPACE) {
    if (!LocalStorage.getKeychainItem) {
      return null;
    }

    try {
      const result = await LocalStorage.getKeychainItem(key, namespace);

      return parseJsonIfNeeded(result);
    } catch (error) {
      throwNativeStorageError(
        storage,
        getItemError(error, true),
        key,
        namespace
      );
    }
  }

  async setKeychainItem(key, value, namespace = DEFAULT_NAMESPACE) {
    try {
      const result = await LocalStorage.setKeychainItem(
        key,
        stringifyIfNeeded(value),
        namespace
      );

      return !!result;
    } catch (error) {
      throwNativeStorageError(
        storage,
        setItemError(error, true),
        key,
        namespace
      );
    }
  }

  async removeKeychainItem(key, namespace = DEFAULT_NAMESPACE) {
    try {
      const result = await LocalStorage.removeKeychainItem(key, namespace);

      return !!result;
    } catch (error) {
      throwNativeStorageError(
        storage,
        removeItemError(error, true),
        key,
        namespace
      );
    }
  }
}

export const localStorage = LocalStorageModule.instance;
