import React, { createContext, useContext, useEffect, useState, useCallback, useRef } from "react";
import axios from "axios";
import { AuthContext } from "./AuthContext";

export const SSEContext = createContext();

/**
 * SSEProvider manages Server-Sent Events connections and real-time event handling
 * Automatically reconnects on disconnection and provides event subscription methods
 */
const SSEProvider = ({ children }) => {
  const { auth } = useContext(AuthContext);
  const [eventSource, setEventSource] = useState(null);
  const [connected, setConnected] = useState(false);
  const [notifications, setNotifications] = useState([]);
  const eventHandlersRef = useRef(new Map());
  const reconnectTimeoutRef = useRef(null);
  const reconnectAttemptsRef = useRef(0);

  const MAX_RECONNECT_ATTEMPTS = 5;
  const RECONNECT_DELAY = 1000;

  // Clean up reconnect timeout on unmount
  useEffect(() => {
    return () => {
      if (reconnectTimeoutRef.current) {
        clearTimeout(reconnectTimeoutRef.current);
      }
    };
  }, []);

  // Connect to SSE stream
  const connectSSE = useCallback(() => {
    if (!auth.isAuthenticated || !auth.token) {
      console.log("SSE: Not authenticated, skipping connection");
      return;
    }

    console.log("SSE: Connecting to event stream");
    const url = `/api/sse/stream?token=${encodeURIComponent(auth.token)}`;
    const es = new EventSource(url);

    es.onopen = () => {
      console.log("SSE: Connection established");
      setConnected(true);
      reconnectAttemptsRef.current = 0;
    };

    es.onerror = (error) => {
      console.error("SSE: Connection error:", error);
      setConnected(false);
      es.close();
      setEventSource(null);

      // Attempt reconnection with exponential backoff
      if (reconnectAttemptsRef.current < MAX_RECONNECT_ATTEMPTS) {
        reconnectAttemptsRef.current += 1;
        const delay = RECONNECT_DELAY * Math.pow(2, reconnectAttemptsRef.current - 1);
        console.log(`SSE: Reconnecting in ${delay}ms (attempt ${reconnectAttemptsRef.current}/${MAX_RECONNECT_ATTEMPTS})`);
        
        reconnectTimeoutRef.current = setTimeout(() => {
          if (auth.isAuthenticated) {
            connectSSE();
          }
        }, delay);
      } else {
        console.error("SSE: Max reconnection attempts reached");
      }
    };

    // Handle incoming messages
    es.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        console.log("SSE: Received message:", data);

        const { type, payload } = data;

        // Add to notifications array
        if (type === "notification" || !type) {
          setNotifications((prev) => [
            ...prev,
            {
              id: Date.now(),
              timestamp: new Date(),
              ...payload,
            },
          ]);
        }

        // Call registered event handlers
        if (type) {
          const handlers = eventHandlersRef.current.get(type);
          if (handlers && handlers.size > 0) {
            handlers.forEach((callback) => {
              try {
                callback(payload || data);
              } catch (error) {
                console.error(`SSE: Error in event handler for ${type}:`, error);
              }
            });
          }
        }
      } catch (error) {
        console.error("SSE: Error parsing message:", error, event.data);
      }
    };

    setEventSource(es);
  }, [auth.isAuthenticated, auth.token]);

  // Connect when user is authenticated
  useEffect(() => {
    if (auth.isAuthenticated && auth.token) {
      connectSSE();
    } else {
      // Disconnect when user logs out
      if (eventSource) {
        console.log("SSE: Disconnecting due to logout");
        eventSource.close();
        setEventSource(null);
        setConnected(false);
      }
      if (reconnectTimeoutRef.current) {
        clearTimeout(reconnectTimeoutRef.current);
      }
      reconnectAttemptsRef.current = 0;
    }

    // Cleanup on unmount
    return () => {
      if (eventSource) {
        eventSource.close();
      }
      if (reconnectTimeoutRef.current) {
        clearTimeout(reconnectTimeoutRef.current);
      }
    };
  }, [auth.isAuthenticated, auth.token, connectSSE]);

  // Subscribe to a specific event type
  const on = useCallback((eventType, callback) => {
    if (!eventHandlersRef.current.has(eventType)) {
      eventHandlersRef.current.set(eventType, new Set());
    }
    eventHandlersRef.current.get(eventType).add(callback);
    console.log(`SSE: Registered handler for event type: ${eventType}`);
  }, []);

  // Unsubscribe from a specific event type
  const off = useCallback((eventType, callback) => {
    const handlers = eventHandlersRef.current.get(eventType);
    if (handlers) {
      handlers.delete(callback);
      if (handlers.size === 0) {
        eventHandlersRef.current.delete(eventType);
      }
      console.log(`SSE: Unregistered handler for event type: ${eventType}`);
    }
  }, []);

  // Subscribe to specific topics via backend
  const subscribe = useCallback(
    async (topics) => {
      if (!auth.isAuthenticated) {
        console.warn("SSE: Cannot subscribe - not authenticated");
        return;
      }

      try {
        await axios.post("/api/sse/subscribe", { topics });
        console.log("SSE: Subscribed to topics:", topics);
      } catch (error) {
        console.error("SSE: Error subscribing to topics:", error);
      }
    },
    [auth.isAuthenticated]
  );

  // Unsubscribe from specific topics via backend
  const unsubscribe = useCallback(
    async (topics) => {
      if (!auth.isAuthenticated) {
        console.warn("SSE: Cannot unsubscribe - not authenticated");
        return;
      }

      try {
        await axios.post("/api/sse/unsubscribe", { topics });
        console.log("SSE: Unsubscribed from topics:", topics);
      } catch (error) {
        console.error("SSE: Error unsubscribing from topics:", error);
      }
    },
    [auth.isAuthenticated]
  );

  // Clear a notification
  const clearNotification = useCallback((notificationId) => {
    setNotifications((prev) =>
      prev.filter((notification) => notification.id !== notificationId)
    );
  }, []);

  // Clear all notifications
  const clearAllNotifications = useCallback(() => {
    setNotifications([]);
  }, []);

  const value = {
    eventSource,
    connected,
    notifications,
    eventHandlers: eventHandlersRef.current,
    on,
    off,
    subscribe,
    unsubscribe,
    clearNotification,
    clearAllNotifications,
  };

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

export default SSEProvider;

/**
 * Custom hook to use SSE context
 * @returns {Object} SSE context value
 */
export const useSSE = () => {
  const context = useContext(SSEContext);
  if (!context) {
    throw new Error("useSSE must be used within an SSEProvider");
  }
  return context;
};
