import type { ReactNode } from 'react';
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { NotificationModel, NotificationsOptions, NotifyInput } from './types';

type NotificationsApi = {
  options: Required<NotificationsOptions>;
  setOptions: (next: Partial<NotificationsOptions>) => void;
  notifications: NotificationModel[];
  notify: (input: NotifyInput) => string;
  clearAll: () => void;
  clearById: (id: string) => void;
};

const NotificationsContext = createContext<NotificationsApi | null>(null);

const DEFAULTS: Required<NotificationsOptions> = {
  position: 'toast-top-right',
  newestOnTop: true,
  preventDuplicates: false,
  closeButton: false,
  progressBar: false,
  showDurationMs: 300,
  hideDurationMs: 1000,
  showMethod: 'fade',
  hideMethod: 'fade',
  pauseOnHover: true,
};

function stableId() {
  return `ntf_${Math.random().toString(36).slice(2)}_${Date.now().toString(36)}`;
}

export function NotificationsProvider({ children }: { children: ReactNode }) {
  const [options, setOptionsState] = useState<Required<NotificationsOptions>>(DEFAULTS);
  const optionsRef = useRef(options);
  const [notifications, setNotifications] = useState<NotificationModel[]>([]);

  const lastMessageKey = useRef<string | null>(null);

  useLayoutEffect(() => {
    optionsRef.current = options;
  }, [options]);

  const setOptions = useCallback((next: Partial<NotificationsOptions>) => {
    const merged: Required<NotificationsOptions> = { ...optionsRef.current, ...next };
    optionsRef.current = merged;
    setOptionsState(merged);
  }, []);

  const clearById = useCallback((id: string) => {
    setNotifications((prev) => prev.filter((n) => n.id !== id));
  }, []);

  const clearAll = useCallback(() => {
    setNotifications([]);
  }, []);

  const notify = useCallback((input: NotifyInput) => {
    const o = optionsRef.current;
    const id = input.id ?? stableId();
    const variant = input.variant ?? 'success';
    const title = input.title?.trim() ? input.title : undefined;
    const message = input.message ?? '';
    const durationMs = typeof input.durationMs === 'number' ? input.durationMs : 5000;

    const dupKey = `${variant}|${title ?? ''}|${message}`;
    if (o.preventDuplicates && lastMessageKey.current === dupKey) return id;
    lastMessageKey.current = dupKey;

    const model: NotificationModel = {
      id,
      title,
      message,
      variant,
      createdAt: Date.now(),
      durationMs,
      position: o.position,
      closeButton: o.closeButton,
      progressBar: o.progressBar,
    };

    setNotifications((prev) => {
      const next = o.newestOnTop ? [model, ...prev] : [...prev, model];
      return next;
    });

    return id;
  }, []);

  const value = useMemo<NotificationsApi>(
    () => ({ options, setOptions, notifications, notify, clearAll, clearById }),
    [clearAll, clearById, notifications, notify, options, setOptions]
  );

  return <NotificationsContext.Provider value={value}>{children}</NotificationsContext.Provider>;
}

export function useNotifications() {
  const ctx = useContext(NotificationsContext);
  if (!ctx) throw new Error('useNotifications must be used within NotificationsProvider');
  return ctx;
}

