'use client';

import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { v4 as uuid } from 'uuid';

import { ModalNotification } from './ModalNotification';
import { ToastNotification } from './ToastNotification';

import { Stack } from '@/ui/primitives/Stack';

import type { Notification, NotificationAction, NotificationVariant } from './notificationTypes';

interface AddNotificationInput {
  id?: string;
  type: string;
  title?: string;
  message: string;
  createdAt?: Date;
  variant?: NotificationVariant;
  actionUrl?: string;
  requiresResponse?: boolean;
  actions?: NotificationAction[];
  data?: Record<string, unknown>;
}

interface NotificationContextValue {
  notifications: Notification[];
  unreadCount: number;
  addNotification: (input: AddNotificationInput) => string;
  dismissNotification: (id: string) => void;
  clearNotifications: () => void;
  markAsRead: (id: string) => void;
  markAllAsRead: () => void;
}

const NotificationContext = createContext<NotificationContextValue | null>(null);

export function useNotifications() {
  const context = useContext(NotificationContext);
  if (!context) {
    throw new Error('useNotifications must be used within NotificationProvider');
  }
  return context;
}

interface NotificationProviderProps {
  children: ReactNode;
}

export function NotificationProvider({ children }: NotificationProviderProps) {
  const [notifications, setNotifications] = useState<Notification[]>([]);

  const addNotification = useCallback((input: AddNotificationInput): string => {
    const id = input.id ?? uuid();

    const notification: Notification = {
      id,
      type: input.type,
      title: input.title,
      message: input.message,
      createdAt: input.createdAt ?? new Date(),
      variant: input.variant ?? 'toast',
      actionUrl: input.actionUrl,
      requiresResponse: input.requiresResponse,
      actions: input.actions,
      data: input.data,
      read: false,
    };

    setNotifications((prev) => [notification, ...prev]);

    return id;
  }, []);

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

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

  const markAsRead = useCallback((id: string) => {
    setNotifications((prev) =>
      prev.map((notification) =>
        notification.id === id ? { ...notification, read: true } : notification,
      ),
    );
  }, []);

  const markAllAsRead = useCallback(() => {
    setNotifications((prev) => prev.map((notification) => ({ ...notification, read: true })));
  }, []);

  const unreadCount = useMemo(
    () => notifications.filter((notification) => !notification.read).length,
    [notifications],
  );

  const modalNotification = useMemo(
    () => notifications.find((notification) => notification.variant === 'modal' && !notification.read) ?? null,
    [notifications],
  );

  const toastNotifications = useMemo(
    () => notifications.filter((notification) => notification.variant === 'toast' && !notification.read),
    [notifications],
  );

  useEffect(() => {
    if (!modalNotification) {
      return;
    }

    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';

    return () => {
      document.body.style.overflow = previousOverflow;
    };
  }, [modalNotification]);

  const value: NotificationContextValue = {
    notifications,
    unreadCount,
    addNotification,
    dismissNotification,
    clearNotifications,
    markAsRead,
    markAllAsRead,
  };

  return (
    <NotificationContext.Provider value={value}>
      {children}

      {/* Toast notifications container */}
      <Stack position="fixed" top="20" right="4" zIndex={50}>
        <Stack gap={3}>
          {toastNotifications.map((notification) => (
            <ToastNotification
              key={notification.id}
              notification={notification}
              onDismiss={() => dismissNotification(notification.id)}
              onRead={() => markAsRead(notification.id)}
            />
          ))}
        </Stack>
      </Stack>

      {/* Modal notification */}
      {modalNotification && (
        <ModalNotification
          notification={modalNotification}
          onAction={(notification: Notification, actionId?: string) => {
            // For now we just mark as read and optionally navigate via ModalNotification
            markAsRead(notification.id);
            if (actionId === 'dismiss') {
              dismissNotification(notification.id);
            }
          }}
          onDismiss={(notification: Notification) => {
            markAsRead(notification.id);
            dismissNotification(notification.id);
          }}
        />
      )}
    </NotificationContext.Provider>
  );
}
