import { useState, useEffect, useCallback, useMemo } from 'react';
import { useSSE } from '../contexts/SSEContext';
import { calculateSnoozeUntil, type SnoozedAlert } from '../utils/alertHelpers';

export interface NotificationData {
  id: string;
  item_type: 'alert' | 'recommendation';
  severity: 'urgent' | 'high' | 'medium' | 'low';
  title: string;
  message: string;
  timestamp: string;
  read: boolean;
  metadata?: Record<string, any>;
}

const STORAGE_KEY = 'bakery-notifications';
const SNOOZE_STORAGE_KEY = 'bakery-snoozed-alerts';

const loadNotificationsFromStorage = (): NotificationData[] => {
  try {
    const stored = localStorage.getItem(STORAGE_KEY);
    if (stored) {
      const parsed = JSON.parse(stored);
      if (Array.isArray(parsed)) {
        // Clean up old alerts (older than 24 hours)
        // This prevents accumulation of stale alerts in localStorage
        const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000);
        const recentAlerts = parsed.filter(n => {
          const alertTime = new Date(n.timestamp).getTime();
          return alertTime > oneDayAgo;
        });

        // If we filtered out alerts, update localStorage
        if (recentAlerts.length !== parsed.length) {
          console.log(`Cleaned ${parsed.length - recentAlerts.length} old alerts from localStorage`);
          localStorage.setItem(STORAGE_KEY, JSON.stringify(recentAlerts));
        }

        return recentAlerts;
      }
    }
  } catch (error) {
    console.warn('Failed to load notifications from localStorage:', error);
  }
  return [];
};

const saveNotificationsToStorage = (notifications: NotificationData[]) => {
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(notifications));
  } catch (error) {
    console.warn('Failed to save notifications to localStorage:', error);
  }
};

const loadSnoozedAlertsFromStorage = (): Map<string, SnoozedAlert> => {
  try {
    const stored = localStorage.getItem(SNOOZE_STORAGE_KEY);
    if (stored) {
      const parsed = JSON.parse(stored);
      const map = new Map<string, SnoozedAlert>();

      Object.entries(parsed).forEach(([key, value]) => {
        const snoozed = value as SnoozedAlert;
        // Only add if not expired
        if (snoozed.until > Date.now()) {
          map.set(key, snoozed);
        }
      });

      return map;
    }
  } catch (error) {
    console.warn('Failed to load snoozed alerts from localStorage:', error);
  }
  return new Map();
};

const saveSnoozedAlertsToStorage = (snoozedAlerts: Map<string, SnoozedAlert>) => {
  try {
    const obj: Record<string, SnoozedAlert> = {};
    snoozedAlerts.forEach((value, key) => {
      // Only save if not expired
      if (value.until > Date.now()) {
        obj[key] = value;
      }
    });
    localStorage.setItem(SNOOZE_STORAGE_KEY, JSON.stringify(obj));
  } catch (error) {
    console.warn('Failed to save snoozed alerts to localStorage:', error);
  }
};

/**
 * useNotifications - Hook for managing real-time notifications and alerts
 *
 * Features:
 * - SSE connection for real-time alerts
 * - localStorage persistence with auto-cleanup (alerts >24h are removed on load)
 * - Snooze functionality with expiration tracking
 * - Bulk operations (mark multiple as read, remove, snooze)
 *
 * Note: localStorage is automatically cleaned of alerts older than 24 hours
 * on load to prevent accumulation of stale data.
 */
export const useNotifications = () => {
  const [notifications, setNotifications] = useState<NotificationData[]>(() => loadNotificationsFromStorage());
  const [snoozedAlerts, setSnoozedAlerts] = useState<Map<string, SnoozedAlert>>(() => loadSnoozedAlertsFromStorage());
  const [unreadCount, setUnreadCount] = useState(() => {
    const stored = loadNotificationsFromStorage();
    return stored.filter(n => !n.read).length;
  });

  const { addEventListener, isConnected } = useSSE();

  // Save to localStorage whenever notifications change
  useEffect(() => {
    saveNotificationsToStorage(notifications);
  }, [notifications]);

  // Save snoozed alerts to localStorage
  useEffect(() => {
    saveSnoozedAlertsToStorage(snoozedAlerts);
  }, [snoozedAlerts]);

  // Clean up expired snoozed alerts periodically
  useEffect(() => {
    const cleanupInterval = setInterval(() => {
      setSnoozedAlerts(prev => {
        const updated = new Map(prev);
        let hasChanges = false;

        updated.forEach((value, key) => {
          if (value.until <= Date.now()) {
            updated.delete(key);
            hasChanges = true;
          }
        });

        return hasChanges ? updated : prev;
      });
    }, 60 * 1000); // Check every minute

    return () => clearInterval(cleanupInterval);
  }, []);

  useEffect(() => {
    // Listen for initial_items event (existing notifications)
    const removeInitialListener = addEventListener('initial_items', (data: any[]) => {
      if (Array.isArray(data) && data.length > 0) {
        const initialNotifications: NotificationData[] = data.map(item => ({
          id: item.id,
          item_type: item.item_type,
          severity: item.severity,
          title: item.title,
          message: item.message,
          timestamp: item.timestamp || new Date().toISOString(),
          read: false,
          metadata: item.metadata,
        }));

        setNotifications(prev => {
          // Merge initial items with existing notifications, avoiding duplicates
          const existingIds = new Set(prev.map(n => n.id));
          const newNotifications = initialNotifications.filter(n => !existingIds.has(n.id));
          const combined = [...newNotifications, ...prev].slice(0, 50);
          return combined;
        });

        setUnreadCount(prev => {
          const newUnreadCount = initialNotifications.filter(n => !n.read).length;
          return prev + newUnreadCount;
        });
      }
    });

    // Listen for alert events
    const removeAlertListener = addEventListener('alert', (data: any) => {
      const notification: NotificationData = {
        id: data.id,
        item_type: 'alert',
        severity: data.severity,
        title: data.title,
        message: data.message,
        timestamp: data.timestamp || new Date().toISOString(),
        read: false,
        metadata: data.metadata,
      };

      setNotifications(prev => {
        // Check if notification already exists
        const exists = prev.some(n => n.id === notification.id);
        if (exists) return prev;

        return [notification, ...prev].slice(0, 50); // Keep last 50 notifications
      });
      setUnreadCount(prev => prev + 1);
    });

    // Listen for recommendation events
    const removeRecommendationListener = addEventListener('recommendation', (data: any) => {
      const notification: NotificationData = {
        id: data.id,
        item_type: 'recommendation',
        severity: data.severity,
        title: data.title,
        message: data.message,
        timestamp: data.timestamp || new Date().toISOString(),
        read: false,
        metadata: data.metadata,
      };

      setNotifications(prev => {
        // Check if notification already exists
        const exists = prev.some(n => n.id === notification.id);
        if (exists) return prev;

        return [notification, ...prev].slice(0, 50); // Keep last 50 notifications
      });
      setUnreadCount(prev => prev + 1);
    });

    return () => {
      removeInitialListener();
      removeAlertListener();
      removeRecommendationListener();
    };
  }, [addEventListener]);

  const markAsRead = useCallback((notificationId: string) => {
    setNotifications(prev =>
      prev.map(notification =>
        notification.id === notificationId
          ? { ...notification, read: true }
          : notification
      )
    );
    setUnreadCount(prev => Math.max(0, prev - 1));
  }, []);

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

  const removeNotification = useCallback((notificationId: string) => {
    const notification = notifications.find(n => n.id === notificationId);
    setNotifications(prev => prev.filter(n => n.id !== notificationId));

    if (notification && !notification.read) {
      setUnreadCount(prev => Math.max(0, prev - 1));
    }

    // Also remove from snoozed if present
    setSnoozedAlerts(prev => {
      const updated = new Map(prev);
      updated.delete(notificationId);
      return updated;
    });
  }, [notifications]);

  const clearAllNotifications = useCallback(() => {
    setNotifications([]);
    setUnreadCount(0);
  }, []);

  // Snooze an alert
  const snoozeAlert = useCallback((alertId: string, duration: '15min' | '1hr' | '4hr' | 'tomorrow' | number, reason?: string) => {
    const until = calculateSnoozeUntil(duration);

    setSnoozedAlerts(prev => {
      const updated = new Map(prev);
      updated.set(alertId, { alertId, until, reason });
      return updated;
    });
  }, []);

  // Unsnooze an alert
  const unsnoozeAlert = useCallback((alertId: string) => {
    setSnoozedAlerts(prev => {
      const updated = new Map(prev);
      updated.delete(alertId);
      return updated;
    });
  }, []);

  // Check if alert is snoozed
  const isAlertSnoozed = useCallback((alertId: string): boolean => {
    const snoozed = snoozedAlerts.get(alertId);
    if (!snoozed) {
      return false;
    }

    if (snoozed.until <= Date.now()) {
      // Expired, remove it
      setSnoozedAlerts(prev => {
        const updated = new Map(prev);
        updated.delete(alertId);
        return updated;
      });
      return false;
    }

    return true;
  }, [snoozedAlerts]);

  // Get snoozed alerts that are active
  const activeSnoozedAlerts = useMemo(() => {
    const active = new Map<string, SnoozedAlert>();
    snoozedAlerts.forEach((value, key) => {
      if (value.until > Date.now()) {
        active.set(key, value);
      }
    });
    return active;
  }, [snoozedAlerts]);

  // Bulk operations
  const markMultipleAsRead = useCallback((notificationIds: string[]) => {
    const idsSet = new Set(notificationIds);

    setNotifications(prev =>
      prev.map(notification =>
        idsSet.has(notification.id)
          ? { ...notification, read: true }
          : notification
      )
    );

    const unreadToMark = notifications.filter(n => idsSet.has(n.id) && !n.read).length;
    setUnreadCount(prev => Math.max(0, prev - unreadToMark));
  }, [notifications]);

  const removeMultiple = useCallback((notificationIds: string[]) => {
    const idsSet = new Set(notificationIds);

    const unreadToRemove = notifications.filter(n => idsSet.has(n.id) && !n.read).length;

    setNotifications(prev => prev.filter(n => !idsSet.has(n.id)));
    setUnreadCount(prev => Math.max(0, prev - unreadToRemove));

    // Also remove from snoozed
    setSnoozedAlerts(prev => {
      const updated = new Map(prev);
      notificationIds.forEach(id => updated.delete(id));
      return updated;
    });
  }, [notifications]);

  const snoozeMultiple = useCallback((alertIds: string[], duration: '15min' | '1hr' | '4hr' | 'tomorrow' | number) => {
    const until = calculateSnoozeUntil(duration);

    setSnoozedAlerts(prev => {
      const updated = new Map(prev);
      alertIds.forEach(id => {
        updated.set(id, { alertId: id, until });
      });
      return updated;
    });
  }, []);

  return {
    notifications,
    unreadCount,
    isConnected,
    markAsRead,
    markAllAsRead,
    removeNotification,
    clearAll: clearAllNotifications,
    snoozeAlert,
    unsnoozeAlert,
    isAlertSnoozed,
    snoozedAlerts: activeSnoozedAlerts,
    markMultipleAsRead,
    removeMultiple,
    snoozeMultiple,
  };
};
