import { NativeModules } from 'react-native';

const { ReactNativeOlm } = NativeModules;

ReactNativeOlm.initialize();

const log = (..._args: any[]) => {
  console.log(..._args);
};

log(ReactNativeOlm);

const Olm = {
  init: ReactNativeOlm.initialize,
  get_library_version: ReactNativeOlm.getOlmLibVersion,
  PRIVATE_KEY_LENGTH: (arg = null) => ReactNativeOlm.PRIVATE_KEY_LENGTH(arg),
  SAS: class SAS {
    constructor() {
      // ReactNativeOlm.init();
      ReactNativeOlm.createSAS();
    }
    free() {
      log('SAS free');
      const result = ReactNativeOlm.freeSAS();
      log('SAS free FINISHED');
      return result;
    }
    get_pubkey() {
      log('SAS get_pubkey');
      const result = ReactNativeOlm.getSASPubKey();
      log('SAS get_pubkey FINISHED');
      return result;
    }
    set_their_key(their_key: string) {
      log('SAS set_their_key');
      const result = ReactNativeOlm.setTheirSASKey(their_key);
      log('SAS set_their_key FINISHED');
      return result;
    }
    generate_bytes(info: string, length: number) {
      log('SAS generate_bytes');
      const result = new Uint8Array(
        ReactNativeOlm.generateSASBytes(info, length)
      );
      log('SAS generate_bytes FINISHED');
      return result;
    }
    calculate_mac(input: string, info: string) {
      log('SAS calculate_mac');
      const result = ReactNativeOlm.calculateSASMac(input, info);
      log('SAS calculate_mac FINISHED');
      return result;
    }
    calculate_mac_long_kdf(input: string, info: string) {
      log('SAS calculate_mac_long_kdf');
      const result = ReactNativeOlm.calculateSASMacLongKdf(input, info);
      log('SAS calculate_mac_long_kdf FINISHED');
      return result;
    }
  },
  Session: class Session {
    constructor() {
      ReactNativeOlm.createSession();
      log('OlmSession');
      // return OlmSession;
    }
    free() {
      log('Session: free');
      const result = ReactNativeOlm.freeSession();
      log('Session: free finished');
      return result;
    }
    pickle(key: string | Uint8Array) {
      log('Session: pickle');
      const result = ReactNativeOlm.pickleSession(key);
      log('Session: pickle finished');
      return result;
    }
    unpickle(key: string | Uint8Array, pickle: string) {
      log('Session: unpickle');
      const result = ReactNativeOlm.unpickleSession(key, pickle);
      log('Session: unpickle FINISHED');
      return result;
    }
    create_outbound(
      account: any,
      their_identity_key: string,
      their_one_time_key: string
    ) {
      log('Session: create_outbound');
      const result = ReactNativeOlm.createOutboundSession(
        account,
        their_identity_key,
        their_one_time_key
      );
      log('Session: create_outbound FINISHED');
      return result;
    }
    create_inbound(account: any, one_time_key_message: string) {
      log('Session: create_inbound');
      const result = ReactNativeOlm.createInboundSession(
        account,
        one_time_key_message
      );
      log('Session: create_inbound FINISHED');
      return result;
    }
    create_inbound_from(
      account: any,
      identity_key: string,
      one_time_key_message: string
    ) {
      log('Session: create_inbound_from');
      const result = ReactNativeOlm.createInboundSessionFrom(
        account,
        identity_key,
        one_time_key_message
      );
      log('Session: create_inbound_from FINISHED');
      return result;
    }
    session_id() {
      log('Session: session_id');
      const result = ReactNativeOlm.getSessionId();
      log('Session: session_id FINISHED');
      return result;
    }
    has_received_message() {
      log('Session: has_received_message');
      const result = ReactNativeOlm.sessionHasReceivedMessage();
      log('Session: has_received_message FINISHED');
      return result;
    }
    matches_inbound(one_time_key_message: string) {
      log('Session: matches_inbound');
      const result = ReactNativeOlm.sessionMatchesInbound(one_time_key_message);
      log('Session: matches_inbound FINISHED');
      return typeof result === 'string' ? result === 'true' : result;
    }
    matches_inbound_from(identity_key: string, one_time_key_message: string) {
      log('Session: matches_inbound_from');
      const result = ReactNativeOlm.sessionMatchesInboundFrom(
        identity_key,
        one_time_key_message
      );
      log('Session: matches_inbound_from FINISHED');
      return typeof result === 'string' ? result === 'true' : result;
    }
    encrypt(plaintext: string) {
      log('Session: encrypt');
      const result = ReactNativeOlm.encryptSession(plaintext);
      log('Session: encrypt FINISHED');
      if (result?.error) {
        throw new Error(result?.errorMessage);
      }
      return result;
    }
    decrypt(message_type: number, message: string) {
      log('Session: decrypt');
      const result = ReactNativeOlm.decryptSession(message_type, message);
      log('Session: decrypt FINISHED');
      if (result?.error) {
        throw new Error(result?.errorMessage);
      }
      return result.result;
    }
    describe() {
      log('Session: describe');
      const result = ReactNativeOlm.describeSession();
      log('Session: describe FINISHED');
      return result;
    }
  },
  Utility: class Utility {
    constructor() {
      log('Utility');
      ReactNativeOlm.createUtility();
    }
    free() {
      log('Utility: free');
      const result = ReactNativeOlm.freeUtility();
      log('Utility: free FINISHED');
      return result;
    }
    sha256(input: string | Uint8Array) {
      log('Utility: sha256');
      const result = ReactNativeOlm.sha256(input);
      log('Utility: sha256 FINISHED');
      return result;
    }
    ed25519_verify(
      key: string,
      message: string | Uint8Array,
      signature: string
    ) {
      log('Utility: ed25519_verify');
      const result = ReactNativeOlm.ed25519_verify(key, message, signature);
      log('Utility: ed25519_verify FINISHED');
      return typeof result === 'string' ? result === 'true' : result;
    }
  },
  PkSigning: class PkSigning {
    constructor() {
      log('PkSigning');
      ReactNativeOlm.createPkSigning();
    }
    free() {
      log('PkSigning: free');
      const result = ReactNativeOlm.freePkSigning();
      log('PkSigning: free FINISHED');
      return result;
    }
    init_with_seed(seed: Uint8Array) {
      log('PkSigning: init_with_seed');
      const result = ReactNativeOlm.initPkSigningWithSeed(Array.from(seed));
      log('PkSigning: init_with_seed FINISHED');
      return result;
    }
    generate_seed() {
      log('PkSigning: generate_seed');
      const result = new Uint8Array(ReactNativeOlm.generatePkSigningSeed());
      log('PkSigning: generate_seed FINISHED');
      return result;
    }
    sign(message: string) {
      log('PkSigning: sign');
      const result = ReactNativeOlm.signPkSigning(message);
      log('PkSigning: sign FINISHED');
      return result;
    }
  },
  PkEncryption: class PkEncryption {
    constructor() {
      ReactNativeOlm.createPkEncryption();
      log('OlmPkEncryption');
    }
    free() {
      log('PkEncryption: free');
      const result = ReactNativeOlm.freePkEncryption();
      log('PkEncryption: free FINISHED');
      return result;
    }
    set_recipient_key(key: string) {
      log('PkEncryption: set_recipient_key');
      const result = ReactNativeOlm.setPkEncryptionRecipientKey(key);
      log('PkEncryption: set_recipient_key FINISHED');
      return result;
    }
    encrypt(plaintext: string) {
      log('PkEncryption: encrypt');
      const result = ReactNativeOlm.encryptPkEncryption(plaintext);
      log('PkEncryption: encrypt FINISHED');
      return result;
    }
  },
  PkDecryption: class PkDecryption {
    constructor() {
      log('OlmPkDecryption');
      ReactNativeOlm.createPkDecryption();
      // return OlmPkDecryption;
    }
    free() {
      log('PkDecryption: free');
      const result = ReactNativeOlm.freePkDecryption();
      log('PkDecryption: free FINISHED');
      return result;
    }
    init_with_private_key(key: Uint8Array) {
      log('PkDecryption: init_with_private_key');
      const result = ReactNativeOlm.initPkDecryptionWithPrivateKey(
        Array.from(key)
      );
      log('PkDecryption: init_with_private_key FINISHED');
      return result;
    }
    generate_key() {
      log('PkDecryption: generate_key');
      const result = ReactNativeOlm.generatePkDecryptionKey();
      log('PkDecryption: generate_key FINISHED');
      return result;
    }
    get_private_key() {
      log('PkDecryption: get_private_key');
      const result = ReactNativeOlm.getPkDecryptionPrivateKey();
      log('PkDecryption: get_private_key FINISHED');
      return result;
    }
    pickle(_key: string | Uint8Array) {
      log('PkDecryption: pickle');
      const result = ReactNativeOlm.picklePkDecryption('DEFAULT_KEY');
      log('PkDecryption: pickle FINISHED');
      return result;
    }
    unpickle(_key: string | Uint8Array, pickle: string) {
      log('PkDecryption: unpickle');
      const result = ReactNativeOlm.unpicklePkDecryption('DEFAULT_KEY', pickle);
      log('PkDecryption: unpickle FINISHED');
      return result;
    }
    decrypt(ephemeral_key: string, mac: string, ciphertext: string) {
      log('PkDecryption: decrypt ================>');
      const result = ReactNativeOlm.decryptPkDecryption(
        ephemeral_key,
        mac,
        ciphertext
      );
      log('PkDecryption: decrypt FINISHED');
      if (result?.error) {
        throw new Error(result?.errorMessage);
      }
      return result.result;
    }
  },
  OutboundGroupSession: class OutboundGroupSession {
    constructor() {
      // return OlmOutboundGroupSession;
      log('OlmOutboundGroupSession');
    }
    free() {
      log('OutboundGroupSession: free');
      const result = ReactNativeOlm.freeOutboundGroupSession();
      log('OutboundGroupSession: free FINISHED');
      return result;
    }
    pickle(_key: string | Uint8Array) {
      log('OutboundGroupSession: pickle');
      const result = ReactNativeOlm.pickleOutboundGroupSession('DEFAULT_KEY');
      log('OutboundGroupSession: pickle FINISHED');
      return result;
    }
    unpickle(_key: string | Uint8Array, pickle: string) {
      log('OutboundGroupSession: unpickle');
      const result = ReactNativeOlm.unpickleOutboundGroupSession(
        'DEFAULT_KEY',
        pickle
      );
      log('OutboundGroupSession: unpickle FINISHED');
      return result;
    }
    create() {
      log('OutboundGroupSession: create');
      const result = ReactNativeOlm.createOutboundGroupSession();
      log('OutboundGroupSession: create FINISHED');
      return result;
    }
    encrypt(plaintext: string) {
      log('OutboundGroupSession: encrypt');
      const result = ReactNativeOlm.encryptOutboundGroupSession(plaintext);
      log('OutboundGroupSession: encrypt FINISHED', result);
      return result;
    }
    session_id() {
      log('OutboundGroupSession: session_id');
      const result = ReactNativeOlm.getOutboundGroupSessionSessionId();
      log('OutboundGroupSession: session_id FINISHED', result);
      return result;
    }
    session_key() {
      log('OutboundGroupSession: session_key');
      const result = ReactNativeOlm.getOutboundGroupSessionSessionKey();
      log('OutboundGroupSession: session_key FINISHED', result);
      return result;
    }
    message_index() {
      log('OutboundGroupSession: message_index');
      const result = ReactNativeOlm.getOutboundGroupSessionMessageIndex();
      log('OutboundGroupSession: message_index FINISHED', result);
      return result;
    }
  },
  InboundGroupSession: class InboundGroupSession {
    constructor() {
      // return OlmInboundGroupSession;
      log('OlmInboundGroupSession');
    }
    free() {
      log('InboundGroupSession: free');
      const result = ReactNativeOlm.freeInboundGroupSession();
      log('InboundGroupSession: free FINISHED');
      return result;
    }
    pickle(_key: string | Uint8Array) {
      log('InboundGroupSession: pickle');
      const result = ReactNativeOlm.pickleInboundGroupSession('DEFAULT_KEY');
      log('InboundGroupSession: pickle FINISHED');
      return result;
    }
    unpickle(_key: string | Uint8Array, pickle: string) {
      log('InboundGroupSession: unpickle');
      const result = ReactNativeOlm.unpickleInboundGroupSession(
        'DEFAULT_KEY',
        pickle
      );
      log('InboundGroupSession: unpickle FINISHED');
      return result;
    }
    create(session_key: string) {
      log('InboundGroupSession: create inboundGroupSession');
      const result = ReactNativeOlm.createInboundGroupSession(session_key);
      log('InboundGroupSession: create inboundGroupSession FINISHED');
      return result;
    }
    import_session(session_key: string) {
      log('InboundGroupSession: import_session');
      const result = ReactNativeOlm.importInboundGroupSession(session_key);
      log('InboundGroupSession: import_session FINISHED');
      if (result?.error) {
        throw new Error(result?.error);
      }
    }
    decrypt(message: string) {
      log('InboundGroupSession: decrypt inboundGroupSession');
      const result = ReactNativeOlm.decryptInboundGroupSession(message, null);
      log('InboundGroupSession: decrypt inboundGroupSession FINISHED');
      if (result?.error) {
        throw new Error(result?.errorMessage);
      }
      return result;
    }
    session_id() {
      log('InboundGroupSession: session_id inboundGroupSession');
      const result = ReactNativeOlm.getInboundGroupSessionSessionId(null);
      log('InboundGroupSession: session_id inboundGroupSession FINISHED');
      if (result?.error) {
        throw new Error(result?.errorMessage);
      }
      return result.result;
    }
    first_known_index() {
      log('InboundGroupSession: inboundGroupSession first_known_index');
      const result = ReactNativeOlm.getInboundGroupSessionFirstKnownIndex(null);
      log(
        'InboundGroupSession: inboundGroupSession first_known_index FINISHED'
      );
      if (result?.error) {
        throw new Error(result?.errorMessage);
      }
      return result.result;
    }
    export_session(message_index: number) {
      log('InboundGroupSession: export_session');
      const result = ReactNativeOlm.exportInboundGroupSession(message_index);
      log('InboundGroupSession: export_session FINISHED');
      if (result?.error) {
        throw new Error(result?.errorMessage);
      }
      return result.result;
    }
  },
  Account: class Account {
    constructor() {
      // return OlmAccount;
      this.create();
      log('OlmAccount');
    }
    free() {
      log('Account: free');
      const result = ReactNativeOlm.freeAccount();
      log('Account: free FINISHED');
      return result;
    }
    create() {
      log('Account: create');
      const result = ReactNativeOlm.createAccount();
      log('Account: create FINISHED');
      return result;
    }
    identity_keys() {
      log('Account: identity_keys');
      const result = ReactNativeOlm.identity_keys(null);
      log('Account: identity_keys FINISHED');
      return result;
    }
    sign(message: string | Uint8Array) {
      log('Account: sign');
      const result = ReactNativeOlm.signAccount(message);
      log('Account: sign FINISHED');
      return result;
    }
    one_time_keys() {
      log('Account: one_time_keys');
      const result = ReactNativeOlm.one_time_keys(null);
      log('Account: one_time_keys FINISHED');
      return result;
    }
    mark_keys_as_published() {
      log('Account: mark_keys_as_published');
      const result = ReactNativeOlm.mark_keys_as_published();
      log('Account: mark_keys_as_published FINISHED');
      return result;
    }
    max_number_of_one_time_keys() {
      log('Account: max_number_of_one_time_keys');
      const result = ReactNativeOlm.max_number_of_one_time_keys(null);
      log('Account: max_number_of_one_time_keys FINISHED');
      return result;
    }
    generate_one_time_keys(number_of_keys: number) {
      log('Account: generate_one_time_keys');
      const result = ReactNativeOlm.generate_one_time_keys(number_of_keys);
      log('Account: generate_one_time_keys FINISHED');
      return result;
    }
    remove_one_time_keys(_session: any) {
      log('Account: remove_one_time_keys UNIMPLEMENTED');
      // const result = ReactNativeOlm.remove_one_time_keys('session'); // FIXME LATER
      // log('Account: remove_one_time_keys UNIMPLEMENTED');
      // return result;
    }
    generate_fallback_key() {
      log('Account: generate_fallback_key');
      // FIXME I couldn't find this function in the iOS OLMKit
      const result = ReactNativeOlm.generate_fallback_key();
      log('Account: generate_fallback_key FINISHED');
      return result;
    }
    fallback_key() {
      log('Account: fallback_key');
      // FIXME I couldn't find this function in the iOS OLMKit
      const result = ReactNativeOlm.fallback_key();
      log('Account: fallback_key FINISHED');
      return result;
    }
    pickle(_key: string | Uint8Array = 'DEFAULT_KEY') {
      log('Account: pickle');
      const result = ReactNativeOlm.pickleAccount(_key);
      log('Account: pickle FINISHED');
      return result;
    }
    unpickle(
      _key: string | Uint8Array = 'DEFAULT_KEY',
      pickle: string = 'PICKLE'
    ) {
      log('Account: unpickle');
      const result = ReactNativeOlm.unpickleAccount(_key, pickle);
      log('Account: unpickle FINISHED');
      return result;
    }
  },
};

export default Olm;
