import { useRef, useEffect } from 'react';
import { BackHandler } from 'react-native';
import ToastHybrid from './NativeToast';

export interface ToastConfig {
  backgroundColor?: string
  tintColor?: string
  fontSize?: number
  cornerRadius?: number
  duration?: number
  graceTime?: number
  minShowTime?: number
  loadingText?: string
}

let defaultDuration = 2000;

export default class Toast {
  private static instances = new Set<Toast>();

  static config(options: ToastConfig = {}) {
    defaultDuration = options.duration || defaultDuration;
    ToastHybrid.config(options);
  }

  static text(text: string, duration = defaultDuration) {
    new Toast().text(text, duration);
  }

  static info(text: string, duration = defaultDuration) {
    new Toast().info(text, duration);
  }

  static done(text: string, duration = defaultDuration) {
    new Toast().done(text, duration);
  }

  static error(text: string, duration = defaultDuration) {
    new Toast().error(text, duration);
  }

  static loading(text?: string) {
    return new Toast().loading(text);
  }

  static hideAll() {
    Array.from(Toast.instances).forEach(t => t.hide());
  }

  private underlying: Promise<number> | null = null;

  constructor() {
    Toast.instances.add(this);
  }

  private ensure(): Promise<number> {
    if (this.underlying !== null) {
      return this.underlying;
    }
    const underlying = ToastHybrid.createToast();
    return underlying;
  }

  private closed = false;
  private timer: number | null = null;

  loading(text?: string) {
    if (!this.closed) {
      this.clearTimeout();
      this.underlying = this.ensure();
      this.underlying.then(key => {
        this.clearTimeout();
        ToastHybrid.loading(key, text);
      });
    }
    return this;
  }

  private clearTimeout() {
    if (this.timer !== null) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }

  text(text: string, duration = defaultDuration) {
    return this.show(ToastHybrid.text, text, duration);
  }

  private show(fn: (key: number, text: string) => void, text: string, duration: number) {
    if (duration === 0) {
      if (this.underlying === null) {
        return this;
      }
      this.hide();
      return this;
    }
    if (!this.closed) {
      this.clearTimeout();
      this.underlying = this.ensure();
      this.underlying.then(key => {
        if (!this.closed) {
          fn(key, text);
          this.clearTimeout();
          this.timer = <any>setTimeout(() => this.hide(), duration);
        } else {
          this.hide();
        }
      });
    }
    return this;
  }

  info(text: string, duration = defaultDuration) {
    return this.show(ToastHybrid.info, text, duration);
  }

  done(text: string, duration = defaultDuration) {
    return this.show(ToastHybrid.done, text, duration);
  }

  error(text: string, duration = defaultDuration) {
    return this.show(ToastHybrid.error, text, duration);
  }

  hide() {
    Toast.instances.delete(this);
    this.clearTimeout();
    if (this.underlying !== null) {
      this.underlying.then(key => {
        ToastHybrid.hide(key);
      });
      this.underlying = null;
    }
  }

  private shutdown() {
    this.closed = true;
    this.hide();
  }
}

export function useToast() {
  const toastRef = useRef(new Toast());
  useEffect(() => {
    const toast = toastRef.current
    ;(toast as any).closed = false;
    return () => {
      (toast as any).shutdown();
    };
  }, []);

  useEffect(() => {
    function handleHardwareBack() {
      const toast = toastRef.current;
      if ((toast as any).underlying !== null) {
        toast.hide();
        return true;
      }
      return false;
    }

    const subscription = BackHandler.addEventListener('hardwareBackPress', handleHardwareBack);

    return () => {
      subscription.remove();
    };
  }, []);

  return toastRef.current;
}
