import React from 'react';
import { useUploadQueue } from '../queue/hooks';
import type { QueueItem } from '../queue/queue';
import {
  dismissGuestNotification,
  fetchGuestNotifications,
  markGuestNotificationRead,
  type GuestNotificationItem,
} from '../services/notificationApi';
import { fetchPendingUploadsSummary } from '../services/pendingUploadsApi';
import { updateAppBadge } from '../lib/badges';

export type NotificationCenterValue = {
  notifications: GuestNotificationItem[];
  unreadCount: number;
  queueItems: QueueItem[];
  queueCount: number;
  pendingCount: number;
  loading: boolean;
  pendingLoading: boolean;
  refresh: () => Promise<void>;
  setFilters: (filters: { status?: 'new' | 'read' | 'dismissed' | 'all'; scope?: 'all' | 'uploads' | 'tips' | 'general' }) => void;
  markAsRead: (id: number) => Promise<void>;
  dismiss: (id: number) => Promise<void>;
  eventToken: string;
  lastFetchedAt: Date | null;
  isOffline: boolean;
};

const NotificationCenterContext = React.createContext<NotificationCenterValue | null>(null);

export function NotificationCenterProvider({ eventToken, children }: { eventToken: string; children: React.ReactNode }) {
  const { items, loading: queueLoading, refresh: refreshQueue } = useUploadQueue();
  const [notifications, setNotifications] = React.useState<GuestNotificationItem[]>([]);
  const [unreadCount, setUnreadCount] = React.useState(0);
  const [loadingNotifications, setLoadingNotifications] = React.useState(true);
  const [pendingCount, setPendingCount] = React.useState(0);
  const [pendingLoading, setPendingLoading] = React.useState(true);
  const [filters, setFiltersState] = React.useState<{ status?: 'new' | 'read' | 'dismissed' | 'all'; scope?: 'all' | 'uploads' | 'tips' | 'general' }>({
    status: 'new',
    scope: 'all',
  });
  const etagRef = React.useRef<string | null>(null);
  const fetchLockRef = React.useRef(false);
  const [lastFetchedAt, setLastFetchedAt] = React.useState<Date | null>(null);
  const [isOffline, setIsOffline] = React.useState<boolean>(typeof navigator !== 'undefined' ? !navigator.onLine : false);

  const queueCount = React.useMemo(
    () => items.filter((item) => item.status !== 'done').length,
    [items]
  );

  const loadNotifications = React.useCallback(
    async (options: { silent?: boolean } = {}) => {
      if (!eventToken) {
        if (!options.silent) {
          setLoadingNotifications(false);
        }
        return;
      }

      if (fetchLockRef.current) {
        return;
      }

      fetchLockRef.current = true;
      if (!options.silent) {
        setLoadingNotifications(true);
      }

      try {
        const statusFilter = filters.status && filters.status !== 'all' ? (filters.status === 'new' ? 'unread' : filters.status) : undefined;
        const result = await fetchGuestNotifications(eventToken, etagRef.current, {
          status: statusFilter as any,
          scope: filters.scope,
        });
        if (!result.notModified) {
          setNotifications(result.notifications);
          setUnreadCount(result.unreadCount);
          setLastFetchedAt(new Date());
        }
        etagRef.current = result.etag;
        setIsOffline(false);
      } catch (error) {
        console.error('Failed to load guest notifications', error);
        if (!options.silent) {
          setNotifications([]);
          setUnreadCount(0);
        }
        if (typeof navigator !== 'undefined' && !navigator.onLine) {
          setIsOffline(true);
        }
      } finally {
        fetchLockRef.current = false;
        if (!options.silent) {
          setLoadingNotifications(false);
        }
      }
    },
    [eventToken]
  );

  const loadPendingUploads = React.useCallback(async () => {
    if (!eventToken) {
      setPendingLoading(false);
      return;
    }

    try {
      setPendingLoading(true);
      const result = await fetchPendingUploadsSummary(eventToken, 1);
      setPendingCount(result.totalCount);
    } catch (error) {
      console.error('Failed to load pending uploads', error);
      setPendingCount(0);
    } finally {
      setPendingLoading(false);
    }
  }, [eventToken]);

  React.useEffect(() => {
    setNotifications([]);
    setUnreadCount(0);
    etagRef.current = null;
    setPendingCount(0);

    if (!eventToken) {
      setLoadingNotifications(false);
      setPendingLoading(false);
      return;
    }

    setLoadingNotifications(true);
    void loadNotifications();
    void loadPendingUploads();
  }, [eventToken, loadNotifications, loadPendingUploads]);

  React.useEffect(() => {
    if (!eventToken) {
      return;
    }

    const interval = window.setInterval(() => {
      void loadNotifications({ silent: true });
      void loadPendingUploads();
    }, 90000);

    return () => window.clearInterval(interval);
  }, [eventToken, loadNotifications, loadPendingUploads]);

  React.useEffect(() => {
    const handleOnline = () => setIsOffline(false);
    const handleOffline = () => setIsOffline(true);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  React.useEffect(() => {
    const handler = (event: MessageEvent) => {
      if (event.data?.type === 'guest-notification-refresh') {
        void loadNotifications({ silent: true });
      }
    };

    navigator.serviceWorker?.addEventListener('message', handler);

    return () => {
      navigator.serviceWorker?.removeEventListener('message', handler);
    };
  }, [loadNotifications]);

  const markAsRead = React.useCallback(
    async (id: number) => {
      if (!eventToken) {
        return;
      }

      let decremented = false;
      setNotifications((prev) =>
        prev.map((item) => {
          if (item.id !== id) {
            return item;
          }

          if (item.status === 'new') {
            decremented = true;
          }

          return {
            ...item,
            status: 'read',
            readAt: new Date().toISOString(),
          };
        })
      );

      if (decremented) {
        setUnreadCount((prev) => Math.max(0, prev - 1));
      }

      try {
        await markGuestNotificationRead(eventToken, id);
      } catch (error) {
        console.error('Failed to mark notification as read', error);
        void loadNotifications({ silent: true });
      }
    },
    [eventToken, loadNotifications]
  );

  const dismiss = React.useCallback(
    async (id: number) => {
      if (!eventToken) {
        return;
      }

      let decremented = false;
      setNotifications((prev) =>
        prev.map((item) => {
          if (item.id !== id) {
            return item;
          }

          if (item.status === 'new') {
            decremented = true;
          }

          return {
            ...item,
            status: 'dismissed',
            dismissedAt: new Date().toISOString(),
          };
        })
      );

      if (decremented) {
        setUnreadCount((prev) => Math.max(0, prev - 1));
      }

      try {
        await dismissGuestNotification(eventToken, id);
      } catch (error) {
        console.error('Failed to dismiss notification', error);
        void loadNotifications({ silent: true });
      }
    },
    [eventToken, loadNotifications]
  );

  const setFilters = React.useCallback((next: { status?: 'new' | 'read' | 'dismissed' | 'all'; scope?: 'all' | 'uploads' | 'tips' | 'general' }) => {
    setFiltersState((prev) => ({ ...prev, ...next }));
    void loadNotifications({ silent: true });
  }, [loadNotifications]);

  const refresh = React.useCallback(async () => {
    await Promise.all([loadNotifications(), refreshQueue(), loadPendingUploads()]);
  }, [loadNotifications, refreshQueue, loadPendingUploads]);

  const loading = loadingNotifications || queueLoading || pendingLoading;
  React.useEffect(() => {
    void updateAppBadge(unreadCount);
  }, [unreadCount]);

  const value: NotificationCenterValue = {
    notifications,
    unreadCount,
    queueItems: items,
    queueCount,
    pendingCount,
    loading,
    pendingLoading,
    refresh,
    setFilters,
    markAsRead,
    dismiss,
    eventToken,
    lastFetchedAt,
    isOffline,
  };

  return (
    <NotificationCenterContext.Provider value={value}>
      {children}
    </NotificationCenterContext.Provider>
  );
}

export function useNotificationCenter(): NotificationCenterValue {
  const ctx = React.useContext(NotificationCenterContext);
  if (!ctx) {
    throw new Error('useNotificationCenter must be used within NotificationCenterProvider');
  }
  return ctx;
}

export function useOptionalNotificationCenter(): NotificationCenterValue | null {
  return React.useContext(NotificationCenterContext);
}
