import { useState, useEffect, useRef } from "react";
import {
  getSharedConnection,
  type ConnectionStatus,
} from "../signalr/connection.js";

export interface LiveSubscription<TParams> {
  eventName: string;
  invokeMethodName: string;
  args: (params: TParams) => unknown[];
}

export interface UseLiveFlightsConfig<TParams> {
  hubUrl: string;
  channelKey: (params: TParams) => string;
  subscription?: LiveSubscription<TParams>;
  onMessage?: (message: unknown) => void | unknown[];
}

export interface UseLiveFlightsResult<TData> {
  data: TData[];
  connectionStatus: ConnectionStatus;
}

/**
 * Generic SSR-safe hook for live flight data via SignalR.
 *
 * During SSR (`typeof window === "undefined"`), returns the initial data
 * without touching SignalR. On the client, subscribes to a SignalR channel
 * and merges incoming messages into state.
 */
export function useLiveFlights<TParams, TData>(
  params: TParams,
  initialData: TData[],
  config: UseLiveFlightsConfig<TParams>,
): UseLiveFlightsResult<TData> {
  const [data, setData] = useState<TData[] | null>(null);
  const [connectionStatus, setConnectionStatus] =
    useState<ConnectionStatus>("idle");

  // Keep a stable reference to initialData for the SSR short-circuit
  const initialDataRef = useRef(initialData);
  initialDataRef.current = initialData;
  const onMessageRef = useRef(config.onMessage);
  onMessageRef.current = config.onMessage;

  const isClient = typeof window !== "undefined";
  const paramsKey = config.channelKey(params);

  useEffect(() => {
    if (!isClient) return;
    // Blank hubUrl = live updates disabled for this deployment. Skip the
    // connection entirely; a deployment that wants live updates sets a
    // real SIGNALR_HUB_URL (same-origin or CORS-enabled).
    if (!config.hubUrl) return;

    const channel = config.subscription?.eventName ?? paramsKey;
    const connection = getSharedConnection({ hubUrl: config.hubUrl });
    setData(null);

    // Sync current status
    setConnectionStatus(connection.status);

    const unsubStatus = connection.onStatusChange((status) => {
      setConnectionStatus(status);
    });

    const unsubChannel = connection.subscribe(channel, (message) => {
      const nextData = onMessageRef.current?.(message);
      if (Array.isArray(nextData)) {
        setData(nextData as TData[]);
        return;
      }
      if (!onMessageRef.current) {
        setData(message as TData[]);
      }
    });

    const subscription = config.subscription;
    if (subscription) {
      void connection.invoke(
        subscription.invokeMethodName,
        ...subscription.args(params),
      );
    }

    return () => {
      unsubChannel();
      unsubStatus();
    };
  }, [isClient, config.hubUrl, config.subscription, paramsKey]);

  if (!isClient) {
    return { data: initialData, connectionStatus: "idle" };
  }

  return { data: data ?? initialData, connectionStatus };
}
