import { useRouter } from "next/router";
import { memo, useCallback, useEffect, useRef, useState } from "react";
import {
  notificationService,
  NotificationType,
} from "../services/notification.service.js";
import styles from "../styles/exos/Notifications.module.scss";
import { MdDone, MdError, MdInfo } from "react-icons/md";
import { BiError } from "react-icons/bi";

function NotificationComp({
  notification,
  classes,
  removeAlert,
  mount,
  fadeIn,
}) {
  const notifIcon = {
    [NotificationType.Success]: <MdDone />,
    [NotificationType.Warning]: <BiError />,
    [NotificationType.Info]: <MdInfo />,
    [NotificationType.Error]: <MdError />,
  };
  return (
    <div
      className={classes + " " + (mount && styles["fadeOut"])}
      onClick={() => {
        removeAlert(notification);
      }}
    >
      <div className={styles['notif-content']}>
        <div className={styles["notif-title"]}>
          {notifIcon[notification.type]} {notification.title}
        </div>
        <div className={styles["notif-msg"]}>{notification.message}</div>
      </div>
      </div>
  );
}
const Notification = memo(NotificationComp);

export default function Notifications({ id = "default-notif", fade = true }) {
  const mounted = useRef(false);
  const router = useRouter();
  const [notification, setNotif] = useState([]);

  useEffect(() => {
    mounted.current = true;

    // subscribe to new alert notifications
    const subscription = notificationService.onAlert(id).subscribe((notif) => {
      // clear alerts when an empty alert is received
      if (!notif.message) {
        setNotif((alerts) => {
          // filter out alerts without 'keepAfterRouteChange' flag
          const filteredAlerts = alerts.filter((x) => x.keepAfterRouteChange);

          // remove 'keepAfterRouteChange' flag on the rest
          return omit(filteredAlerts, "keepAfterRouteChange");
        });
      } else {
        // add alert to array with unique id
        notif.itemId = Math.random();

        addNotif(notif);

        // auto close alert if required
        if (notif.autoClose) {
          setTimeout(() => removeAlert(notif), 3000);
        }
      }
    });

    // clear alerts on location change
    const clearAlerts = () => notificationService.clear(id);
    router.events.on("routeChangeStart", clearAlerts);

    // clean up function that runs when the component unmounts
    return () => {
      mounted.current = false;

      // unsubscribe to avoid memory leaks
      subscription.unsubscribe();
      router.events.off("routeChangeStart", clearAlerts);
    };

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  function omit(arr, key) {
    return arr.map((obj) => {
      const { [key]: omitted, ...rest } = obj;
      return rest;
    });
  }

  function addNotif(notif) {
    if (notification.length > 5) {
      removeAlert(notification[0]);
    }
    setNotif((alerts) => [...alerts, notif]);
  }

  function removeAlertFunc(notif) {
    if (!mounted.current) return;
    if (fade) {
      // fade out alert
      setNotif((alerts) =>
        alerts.map((x) =>
          x.itemId === notif.itemId ? { ...x, fade: true } : x
        )
      );

      // remove alert after faded out
      setTimeout(() => {
        setNotif((alerts) => alerts.filter((x) => x.itemId !== notif.itemId));
      }, 500);
    } else {
      // remove alert
      setNotif((alerts) => alerts.filter((x) => x.itemId !== notif.itemId));
    }
  }
  const removeAlert = useCallback(removeAlertFunc, [fade]);
  
  function cssClasses(notif) {
    if (!notif) return;

    const classes = [styles["notif"], styles["notif-dismissable"]];

    const notifTypeClass = {
      [NotificationType.Success]: styles["notif-success"],
      [NotificationType.Error]: styles["notif-danger"],
      [NotificationType.Info]: styles["notif-info"],
      [NotificationType.Warning]: styles["notif-warning"],
    };
    classes.push(notifTypeClass[notif.type]);

    if (notif.fade) {
      classes.push("fade");
    }

    return classes.join(" ");
  }
  useEffect(() => {
    if (!mounted.current) return;
    if (notification.length > 5) {
      removeAlert(notification[0]);
    }
  }, [notification, removeAlert]);

  if (!notification.length) return null;

  return (
    <div className={styles["notifications-container"]}>
      {notification.map((notification, index) => {
        return (
          <Notification
            removeAlert={removeAlert}
            notification={notification}
            classes={cssClasses(notification)}
            key={index}
            mount={notification.fade}
          />
        );
      })}
    </div>
  );
}
