import { NotificationProps } from './types';

const createUUID = (): string => {
  const pattern = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
  return pattern.replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0;
    const v = c === 'x' ? r : ((r & 0x3) | 0x8);
    return v.toString(16);
  });
}

const CONSTANTS = {
  CHANGE: 'change',
  INFO: 'info',
  SUCCESS: 'success',
  WARNING: 'warning',
  ERROR: 'error',
};

const listNotify: NotificationProps[] = [];
const listeners = new Set<(list: NotificationProps[]) => void>();

const create = (notify: Partial<NotificationProps>): void => {
  const defaultNotify: NotificationProps = {
    id: createUUID(),
    type: 'info',
    title: null,
    message: null,
    timeOut: 5000,
    onClick: () => { },
    priority: false,
  };

  const newNotify: NotificationProps = {
    ...defaultNotify,
    ...notify,
    id: notify.id || createUUID(),
  };

  if (notify.priority) {
    listNotify.unshift(newNotify);
  } else {
    listNotify.push(newNotify);
  }

  emitChange();
}

const info = (message: string, title: string | null, timeOut: number, onClick: () => void, priority: boolean): void => {
  create({
    type: CONSTANTS.INFO,
    message,
    title,
    timeOut,
    onClick,
    priority,
  });
}

const success = (message: string, title: string | null, timeOut: number, onClick: () => void, priority: boolean): void => {
  create({
    type: CONSTANTS.SUCCESS,
    message,
    title,
    timeOut,
    onClick,
    priority,
  });
}

const warning = (message: string, title: string | null, timeOut: number, onClick: () => void, priority: boolean): void => {
  create({
    type: CONSTANTS.WARNING,
    message,
    title,
    timeOut,
    onClick,
    priority,
  });
}

const error = (message: string, title: string | null, timeOut: number, onClick: () => void, priority: boolean): void => {
  create({
    type: CONSTANTS.ERROR,
    message,
    title,
    timeOut,
    onClick,
    priority,
  });
}

const remove = (notification: NotificationProps): void => {
  const newListNotify = listNotify.filter((n) => notification.id !== n.id);
  listNotify.length = 0;
  newListNotify.forEach((n) => listNotify.push(n));
  emitChange();
}

const removeAll = (): void => {
  listNotify.length = 0;
  emitChange();
}

const emitChange = (): void => {
  listeners.forEach((listener) => listener(listNotify));
}

const addChangeListener = (callback: (listNotify: NotificationProps[]) => void): void => {
  listeners.add(callback);
}

const removeChangeListener = (callback: (listNotify: NotificationProps[]) => void): void => {
  listeners.delete(callback);
}

export default {
  info,
  success,
  warning,
  error,
  remove,
  removeAll,
  addChangeListener,
  removeChangeListener,
};