import { useEffect, useRef } from "react";
import { RealtimeEvent, RealtimeEventType } from "./realtimeEvents";

const CHANNEL_NAME = "bpi_smartschool_realtime_channel";

let sharedBroadcastChannel: BroadcastChannel | null = null;

function getBroadcastChannel(): BroadcastChannel | null {
  if (typeof window === "undefined" || !("BroadcastChannel" in window)) {
    return null;
  }
  if (!sharedBroadcastChannel) {
    try {
      sharedBroadcastChannel = new BroadcastChannel(CHANNEL_NAME);
    } catch {
      sharedBroadcastChannel = null;
    }
  }
  return sharedBroadcastChannel;
}

/**
 * Publish a realtime event to:
 * 1. Same-tab components (window.dispatchEvent)
 * 2. Other tabs on same browser (BroadcastChannel)
 * 3. All other clients/devices via backend SSE (fetch /api/realtime/stream)
 */
export async function publishRealtimeNotification<T = any>(
  type: RealtimeEventType,
  payload: T
): Promise<void> {
  const event: RealtimeEvent<T> = {
    id: `clt-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,
    type,
    payload,
    timestamp: Date.now(),
  };

  // 1. Same-tab notification
  if (typeof window !== "undefined") {
    try {
      window.dispatchEvent(
        new CustomEvent("smartschool:realtime", { detail: event })
      );
    } catch {}
  }

  // 2. Cross-tab notification
  const channel = getBroadcastChannel();
  if (channel) {
    try {
      channel.postMessage(event);
    } catch {}
  }

  // 3. Server-wide broadcast via SSE
  try {
    fetch("/api/realtime/stream", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ type, payload }),
    }).catch(() => {
      // Ignore background push errors
    });
  } catch {}
}

/**
 * React hook to listen to realtime events across SSE, BroadcastChannel, and window events.
 */
export function useRealtimeSubscription(
  onEvent: (event: RealtimeEvent) => void,
  options?: {
    eventTypes?: RealtimeEventType[];
    revalidateOnFocus?: boolean;
    pollIntervalMs?: number;
    onRevalidate?: () => void;
  }
) {
  const handlerRef = useRef(onEvent);
  handlerRef.current = onEvent;

  const revalidateRef = useRef(options?.onRevalidate);
  revalidateRef.current = options?.onRevalidate;

  useEffect(() => {
    if (typeof window === "undefined") return;

    const shouldHandle = (evt: RealtimeEvent) => {
      if (!options?.eventTypes || options.eventTypes.length === 0) return true;
      return options.eventTypes.includes(evt.type);
    };

    // 1. Same-tab listener
    const handleWindowCustomEvent = (e: Event) => {
      const customEvt = e as CustomEvent<RealtimeEvent>;
      if (customEvt.detail && shouldHandle(customEvt.detail)) {
        handlerRef.current(customEvt.detail);
      }
    };
    window.addEventListener("smartschool:realtime", handleWindowCustomEvent);

    // 2. Cross-tab BroadcastChannel listener
    const channel = getBroadcastChannel();
    const handleBroadcastMessage = (e: MessageEvent<RealtimeEvent>) => {
      if (e.data && shouldHandle(e.data)) {
        handlerRef.current(e.data);
      }
    };
    if (channel) {
      channel.addEventListener("message", handleBroadcastMessage);
    }

    // 3. Server-Sent Events (SSE) connection
    let eventSource: EventSource | null = null;
    let reconnectTimeout: NodeJS.Timeout | null = null;
    let isCleanedUp = false;

    const connectSSE = () => {
      if (isCleanedUp) return;
      try {
        eventSource = new EventSource("/api/realtime/stream");

        eventSource.onmessage = (e) => {
          if (!e.data || e.data.trim() === "" || e.data.startsWith(":")) return;
          try {
            const parsed: RealtimeEvent = JSON.parse(e.data);
            if (parsed && parsed.type && shouldHandle(parsed)) {
              handlerRef.current(parsed);
            }
          } catch {}
        };

        eventSource.onerror = () => {
          if (eventSource) {
            eventSource.close();
            eventSource = null;
          }
          if (!isCleanedUp) {
            // Reconnect after 3 seconds
            reconnectTimeout = setTimeout(connectSSE, 3000);
          }
        };
      } catch {
        if (!isCleanedUp) {
          reconnectTimeout = setTimeout(connectSSE, 5000);
        }
      }
    };

    connectSSE();

    // 4. Focus & Visibility revalidation
    const handleVisibilityChange = () => {
      if (document.visibilityState === "visible") {
        if (options?.revalidateOnFocus && revalidateRef.current) {
          revalidateRef.current();
        }
      }
    };

    if (options?.revalidateOnFocus) {
      document.addEventListener("visibilitychange", handleVisibilityChange);
      window.addEventListener("focus", handleVisibilityChange);
    }

    // 5. Periodic Smart Polling (fallback or continuous tick)
    let pollInterval: NodeJS.Timeout | null = null;
    if (options?.pollIntervalMs && options.pollIntervalMs > 0) {
      pollInterval = setInterval(() => {
        if (document.visibilityState === "visible" && revalidateRef.current) {
          revalidateRef.current();
        }
      }, options.pollIntervalMs);
    }

    return () => {
      isCleanedUp = true;
      window.removeEventListener("smartschool:realtime", handleWindowCustomEvent);
      if (channel) {
        channel.removeEventListener("message", handleBroadcastMessage);
      }
      if (eventSource) {
        eventSource.close();
      }
      if (reconnectTimeout) {
        clearTimeout(reconnectTimeout);
      }
      if (options?.revalidateOnFocus) {
        document.removeEventListener("visibilitychange", handleVisibilityChange);
        window.removeEventListener("focus", handleVisibilityChange);
      }
      if (pollInterval) {
        clearInterval(pollInterval);
      }
    };
  }, [options?.eventTypes, options?.revalidateOnFocus, options?.pollIntervalMs]);
}
