import { nextTick, onBeforeUnmount, onMounted, reactive } from "vue";

const TOAST_DURATION_MS = 4200;
const toastState = reactive({
  messages: []
});

let nextToastId = 1;

function startToastTimer(message) {
  if (message.duration <= 0 || message.remainingMs <= 0) {
    removeToast(message.id);
    return;
  }

  message.startedAt = Date.now();
  message.timerId = window.setTimeout(() => {
    removeToast(message.id);
  }, message.remainingMs);
}

function normalizeMessage(message) {
  if (message === null || message === undefined) {
    return "";
  }

  if (typeof message === "string") {
    return message.trim();
  }

  return String(message).trim();
}

export function notify(message, type = "info", duration = TOAST_DURATION_MS) {
  const text = normalizeMessage(message);
  if (!text) {
    return null;
  }

  const id = nextToastId;
  nextToastId += 1;

  const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : TOAST_DURATION_MS;
  const toastMessage = {
    id,
    type,
    text,
    createdAt: Date.now(),
    duration: safeDuration,
    remainingMs: safeDuration,
    timerId: null,
    startedAt: null,
    paused: false
  };

  toastState.messages.push(toastMessage);
  startToastTimer(toastMessage);

  return id;
}

export function notifySuccess(message) {
  return notify(message, "success");
}

export function notifyError(message) {
  return notify(message, "error", 5600);
}

export function notifyInfo(message) {
  return notify(message, "info");
}

export function removeToast(id) {
  const index = toastState.messages.findIndex((message) => message.id === id);
  if (index >= 0) {
    if (toastState.messages[index].timerId) {
      window.clearTimeout(toastState.messages[index].timerId);
    }
    toastState.messages.splice(index, 1);
  }
}

export function pauseToast(id) {
  const message = toastState.messages.find((item) => item.id === id);
  if (!message || message.paused) {
    return;
  }

  if (message.timerId) {
    window.clearTimeout(message.timerId);
  }

  const elapsedMs = message.startedAt ? Date.now() - message.startedAt : 0;
  message.remainingMs = Math.max(0, message.remainingMs - elapsedMs);
  message.timerId = null;
  message.paused = true;
}

export function resumeToast(id) {
  const message = toastState.messages.find((item) => item.id === id);
  if (!message || !message.paused) {
    return;
  }

  message.paused = false;
  startToastTimer(message);
}

export function useNotifications() {
  return {
    messages: toastState.messages,
    removeToast,
    pauseToast,
    resumeToast
  };
}

function getNotificationType(element) {
  if (element.classList.contains("error-banner") || element.classList.contains("form-error")) {
    return "error";
  }

  return "success";
}

function collectMessageElements(root = document) {
  const selectors = ".feedback-banner, .form-error";
  const elements = [];

  if (root instanceof Element && root.matches(selectors)) {
    elements.push(root);
  }

  elements.push(...Array.from(root.querySelectorAll?.(selectors) || []));
  return elements;
}

export function useDomNotificationBridge() {
  const lastTextByElement = new WeakMap();
  let observer = null;

  function pushFromElement(element) {
    const text = normalizeMessage(element.textContent);
    if (!text) {
      return;
    }

    if (lastTextByElement.get(element) === text) {
      return;
    }

    lastTextByElement.set(element, text);
    notify(text, getNotificationType(element));
  }

  function scan(root = document) {
    collectMessageElements(root).forEach(pushFromElement);
  }

  onMounted(() => {
    nextTick(() => scan());

    observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === "characterData") {
          const parent = mutation.target.parentElement;
          if (parent) {
            scan(parent);
          }
          return;
        }

        if (mutation.target instanceof Element) {
          scan(mutation.target);
        }

        mutation.addedNodes.forEach((node) => {
          if (node instanceof Element) {
            scan(node);
          }
        });
      });
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
      characterData: true
    });
  });

  onBeforeUnmount(() => {
    observer?.disconnect();
    observer = null;
  });
}
