"use client";

import { SSE_EVENT_TYPES } from "@capakraken/shared";
import { useQueryClient } from "@tanstack/react-query";
import { useEffect, useRef } from "react";

/**
 * Connects to the SSE timeline endpoint and invalidates React Query caches
 * when allocation/project change events arrive.
 */
export function useTimelineSSE() {
  const queryClient = useQueryClient();
  const reconnectTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    let es: EventSource | null = null;
    let reconnectAttempts = 0;

    function connect() {
      es = new EventSource("/api/sse/timeline");

      es.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data as string) as { type: string };

          switch (data.type) {
            case SSE_EVENT_TYPES.ALLOCATION_CREATED:
            case SSE_EVENT_TYPES.ALLOCATION_UPDATED:
            case SSE_EVENT_TYPES.ALLOCATION_DELETED:
            case SSE_EVENT_TYPES.VACATION_CREATED:
            case SSE_EVENT_TYPES.VACATION_UPDATED:
            case SSE_EVENT_TYPES.VACATION_DELETED:
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getEntries"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getEntriesView"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getMyEntriesView"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getHolidayOverlays"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getMyHolidayOverlays"]] });
              void queryClient.invalidateQueries({ queryKey: [["allocation", "list"]] });
              break;

            case SSE_EVENT_TYPES.PROJECT_SHIFTED:
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getEntries"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getEntriesView"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getMyEntriesView"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getHolidayOverlays"]] });
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getMyHolidayOverlays"]] });
              void queryClient.invalidateQueries({ queryKey: [["project", "list"]] });
              break;

            case SSE_EVENT_TYPES.BUDGET_WARNING:
              void queryClient.invalidateQueries({ queryKey: [["timeline", "getBudgetStatus"]] });
              break;

            case SSE_EVENT_TYPES.NOTIFICATION_CREATED:
              void queryClient.invalidateQueries({ queryKey: [["notification", "list"]] });
              void queryClient.invalidateQueries({ queryKey: [["notification", "unreadCount"]] });
              break;

            case SSE_EVENT_TYPES.TASK_ASSIGNED:
            case SSE_EVENT_TYPES.TASK_COMPLETED:
            case SSE_EVENT_TYPES.TASK_STATUS_CHANGED:
              void queryClient.invalidateQueries({ queryKey: [["notification", "listTasks"]] });
              void queryClient.invalidateQueries({ queryKey: [["notification", "taskCounts"]] });
              void queryClient.invalidateQueries({ queryKey: [["notification", "unreadCount"]] });
              void queryClient.invalidateQueries({ queryKey: [["notification", "list"]] });
              break;

            case SSE_EVENT_TYPES.REMINDER_DUE:
              void queryClient.invalidateQueries({ queryKey: [["notification", "list"]] });
              void queryClient.invalidateQueries({ queryKey: [["notification", "unreadCount"]] });
              void queryClient.invalidateQueries({ queryKey: [["notification", "listReminders"]] });
              break;

            case SSE_EVENT_TYPES.PING:
              reconnectAttempts = 0; // Reset on successful ping
              break;
          }
        } catch {
          // Ignore parse errors
        }
      };

      es.onerror = () => {
        es?.close();
        reconnectAttempts++;
        const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
        reconnectTimeout.current = setTimeout(connect, delay);
      };
    }

    connect();

    return () => {
      es?.close();
      if (reconnectTimeout.current) {
        clearTimeout(reconnectTimeout.current);
      }
    };
  }, [queryClient]);
}
