import EventEmitter from 'events';
import { NativeModules, Platform } from 'react-native';
import NativeEmitter from './nativeEmitter';
import type VivochaInterface from './vivochaInterface';

const LINKING_ERROR =
  `The package 'react-native-vivocha-chat-only' 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 NativeVivocha = NativeModules.Vivocha
  ? NativeModules.Vivocha
  : new Proxy(
      {},
      {
        get() {
          throw new Error(LINKING_ERROR);
        },
      }
    );

/**
 * Vivocha React Native SDK class
 */
export default class Vivocha extends EventEmitter implements VivochaInterface {
  static #instance: Vivocha;

  private EVENTS: Array<String | symbol> = [
    'agentdata',
    'persistence',
    'agentpresence',
    'agenttyping',
    'chatackreceived',
    'chatacksent',
    'closeremote',
    'actionsent',
    'messagereceived',
    'messagesent',
    'attachmentreceived',
    'attachmentsent',
    'screenshotsession',
    'terminate',
    'transferred',
    'terminatebutton',
    'chatviewminimized'
  ];

  private actions: Map<
    String,
    (name: string, id: string, data: string) => void
  > = new Map();

  /**
   * Private constructor for singleton class
   */
  private constructor() {
    super();
    this.on('newListener', (event: string | symbol) => {
      console.log('VIVOCHA: new listener for event ' + event.toString());
      if (this.listenerCount(event) === 0) {
        this.registerEvent(event);
      }
    });
    this.on('removeListener', (event: string | symbol) => {
      console.log('VIVOCHA: removed listener for event ' + event.toString());
      if (this.listenerCount(event) === 0) {
        this.unregisterEvent(event);
      }
    });
  }

  private registerEvent(event: string | symbol): void {
    if (!this.EVENTS.includes(event)) {
      return;
    }
    console.log('VIVOCHA: Register event ' + event.toString());
    NativeVivocha.registerEvent(event);
    NativeEmitter?.addListener(event.toString(), (message) => {
      this.emit(event, message);
    });
  }

  private unregisterEvent(event: string | symbol): void {
    if (!this.EVENTS.includes(event)) {
      return;
    }
    console.log('VIVOCHA: Unregister event ' + event.toString());
    NativeVivocha.unregisterEvent(event);
    NativeEmitter?.removeAllListeners(event.toString());
  }

  public get instance(): this {
    return this;
  }

  /**
   * Singleton instance getter
   */
  public static get instance(): Vivocha {
    if (!Vivocha.#instance) {
      Vivocha.#instance = new Vivocha();
    }
    return Vivocha.#instance;
  }

  getDataCollection(): Object {
    return NativeVivocha.getDataCollection();
  }

  setDataCollection(data: Array<Object>): void {
    NativeVivocha.setDataCollection(data);
  }

  getConversation(): Object {
    return NativeVivocha.getConversation();
  }

  setCustomerToken(jwt: string): void {
    NativeVivocha.setCustomerToken(jwt);
  }

  unsetCustomerToken(): void {
    NativeVivocha.unsetCustomerToken();
  }

  createContact(data: Object, type: string, params?: Object): Promise<Object> {
    console.log(data, type);
    return NativeVivocha.createContact(data, type, params);
  }

  getContact(): Object {
    return NativeVivocha.getContact();
  }

  getUnreadMessageCount(): number {
    return NativeVivocha.getUnreadMessageCount();
  }

  hideView(animated?: boolean): void {
    NativeVivocha.hideView(animated);
  }

  showView(animated?: boolean): void {
    NativeVivocha.showView(animated);
  }

  storeSurvey(contactId: string, data: Object): Promise<boolean> {
    return NativeVivocha.storeSurvey(contactId, data);
  }

  terminate(hideView?: boolean): void {
    NativeVivocha.terminate(hideView);
  }

  stopScreenshotSession(): void {
    NativeVivocha.stopScreenshotSession();
  }

  isScreenshotSessionAuthorized(): boolean {
    return NativeVivocha.isScreenshotSessionAuthorized();
  }

  setPushToken(token: string): void {
    if (Platform.OS !== 'ios') {
      throw new Error('Method is available only on IOS');
    }
    console.log(token);
    NativeVivocha.setPushToken(token);
  }

  setPushRegistrationId(regId: string): void {
    if (Platform.OS !== 'android') {
      throw new Error('Method is aveialble only on Android.');
    }
    NativeVivocha.setPushRegistrationId(regId);
  }

  public start(
    accountId: string,
    serviceId: string,
    options?: Object
  ): Promise<Object> {
    return NativeVivocha.start(accountId, serviceId, options);
  }

  public addLocalization(language: String, localization: Object): this {
    NativeVivocha.addLocalization(language, localization);
    return this;
  }

  private addAction(
    code: string,
    callback: (name: string, id: string, data: Object) => void
  ): this {
    if (this.actions.size === 0) {
      NativeEmitter?.addListener('onAction', (event) => {
        console.log('VIVOCHA event: ', event);
        if (this.actions.has(event.code)) {
          const cb = this.actions.get(event.code);
          if (typeof cb === 'function') cb(event.name, event.id, event.data);
        }
      });
    }
    this.actions.set(code, callback);
    return this;
  }

  public onAction(
    code: string,
    callback: (name: string, id: string, data: Object) => void
  ): this {
    NativeVivocha.registerAction(code);
    this.addAction(code, callback);
    return this;
  }

  public createCallBackNow(
    phoneNumber: string,
    data?: Object
  ): Promise<Object> {
    return NativeVivocha.createCallBackNow(phoneNumber, data);
  }

  public createChat(data?: Object): Promise<Object> {
    return NativeVivocha.createChat(data);
  }

  public createVideoChat(data?: Object): Promise<Object> {
    return NativeVivocha.createVideoChat(data);
  }

  public get developerMode(): boolean {
    return NativeVivocha.getDeveloperMode();
  }

  public set developerMode(mode: boolean) {
    NativeVivocha.setDeveloperMode(mode);
  }

  public get theme(): Object {
    return NativeVivocha.getTheme();
  }

  public set theme(theme: Object) {
    NativeVivocha.setTheme(theme);
  }

  public get sideTab(): boolean {
    return NativeVivocha.getSideTab();
  }

  public set sideTab(show: boolean) {
    NativeVivocha.setSideTab(show);
  }

  public set language(language: string) {
    NativeVivocha.setLanguage(language);
  }

  public get agent(): Object {
    return NativeVivocha.getAgent();
  }

  public get media(): Object {
    return NativeVivocha.getMedia();
  }

  public get vvcu(): string {
    return NativeVivocha.getVVCU();
  }

  public get vvct(): string {
    return NativeVivocha.getVVCT();
  }

  public onUrlTapped(callback: (url: string) => boolean): this {
    NativeVivocha.registerUrlTapped();
    NativeEmitter?.removeAllListeners('urlTapped');
    NativeEmitter?.addListener('urlTapped', (message) => {
      NativeVivocha.urlTapped(message?.id, callback(message?.url));
    });
    return this;
  }

  public stop(): boolean {
    return NativeVivocha.stop();
  }

  public sendAction(name: string, id: string, data?: Array<any>): this {
    NativeVivocha.sendAction(name, id, data);
    return this;
  }

  public sendMessage(
    body: string,
    type: string | null = null,
    payload: string | null = null
  ): this {
    NativeVivocha.sendMessage(body, type, payload);
    return this;
  }

  public sendAttachment(
    url: string,
    referenceId: string,
    title: string,
    mimetype: string,
    description: string,
    size: number
  ): this {
    NativeVivocha.sendAttachment(
      url,
      referenceId,
      title,
      mimetype,
      description,
      size
    );
    return this;
  }

  public get SDK_VERSION(): string {
    return NativeVivocha.getSdkVersion();
  }

  public get SDK_NAME(): string {
    return NativeVivocha.getSdkName();
  }

  public get SDK_BASE_URL(): string {
    return NativeVivocha.getSdkBaseUrl();
  }
}
