import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import {
  buildSidebarDataSyncKey,
  getSidebarDataFreshness,
} from "@/components/sidebar/sidebar-sync";
import type { SidebarInitialData } from "@/components/sidebar/types";
import { useConvexSidebarData } from "@/hooks/use-convex-sidebar-data";

export interface SidebarDataResult {
  data: SidebarInitialData;
  isLoading: boolean;
  error: Error | null;
  refetch: () => Promise<unknown>;
  source: "convex-live" | "http-fallback" | "initial";
}

const STABLE_SIDEBAR_CACHE_LIMIT = 12;
const stableSidebarDataCache = new Map<string, SidebarInitialData>();

function getStableSidebarData(syncKey: string, data: SidebarInitialData): SidebarInitialData {
  const cached = stableSidebarDataCache.get(syncKey);
  if (cached) {
    return cached;
  }
  stableSidebarDataCache.set(syncKey, data);
  if (stableSidebarDataCache.size > STABLE_SIDEBAR_CACHE_LIMIT) {
    const oldestKey = stableSidebarDataCache.keys().next().value;
    if (typeof oldestKey === "string") {
      stableSidebarDataCache.delete(oldestKey);
    }
  }
  return data;
}

async function requestSidebarData(workspaceId: string): Promise<SidebarInitialData> {
  const response = await fetch(`/api/sidebar?workspaceId=${workspaceId}`, {
    method: "GET",
    credentials: "include",
  });

  if (!response.ok) {
    const payload = await response.json().catch(() => ({}));
    const message = payload?.error ?? "获取侧边栏数据失败";
    throw new Error(message);
  }

  return response.json();
}

export function useSidebarData(initialData: SidebarInitialData): SidebarDataResult {
  const workspaceId = initialData.activeWorkspaceId;
  const convexSidebar = useConvexSidebarData(workspaceId);
  const [manualSnapshot, setManualSnapshot] = useState<{
    workspaceId: string;
    data: SidebarInitialData;
  } | null>(null);
  const shouldUseHttpFallback =
    !convexSidebar.hasLiveSubscription && convexSidebar.canUseHttpFallback;

  const httpQuery = useQuery({
    queryKey: ["sidebar", workspaceId],
    queryFn: () => requestSidebarData(workspaceId),
    initialData,
    staleTime: 1000 * 60,
    enabled: shouldUseHttpFallback,
  });

  const baseLiveData = useMemo(() => {
    const currentManualSnapshot = manualSnapshot?.workspaceId === workspaceId
      ? manualSnapshot.data
      : null;
    const candidates = [
      initialData,
      httpQuery.data,
      convexSidebar.data,
      currentManualSnapshot,
    ].filter((item): item is SidebarInitialData => Boolean(item));
    return candidates.reduce((selected, candidate) => {
      const selectedFreshness = getSidebarDataFreshness(selected);
      const candidateFreshness = getSidebarDataFreshness(candidate);
      return candidateFreshness >= selectedFreshness ? candidate : selected;
    }, initialData);
  }, [
    convexSidebar.data,
    httpQuery.data,
    initialData,
    manualSnapshot,
    workspaceId,
  ]);
  const liveData = baseLiveData;
  const isLoading =
    convexSidebar.hasLiveSubscription
      ? convexSidebar.isLoading
      : shouldUseHttpFallback
        ? httpQuery.isLoading
        : false;
  const source: SidebarDataResult["source"] = convexSidebar.data
    ? "convex-live"
    : shouldUseHttpFallback && httpQuery.data
      ? "http-fallback"
      : "initial";
  const error = convexSidebar.error ?? httpQuery.error ?? null;
  const liveDataRef = useRef(liveData);
  const convexRefetchRef = useRef(convexSidebar.refetch);
  const httpRefetchRef = useRef(httpQuery.refetch);
  const liveDataSyncKey = useMemo(() => buildSidebarDataSyncKey(liveData), [liveData]);

  useEffect(() => {
    liveDataRef.current = liveData;
  }, [liveData]);

  useEffect(() => {
    convexRefetchRef.current = convexSidebar.refetch;
  }, [convexSidebar]);

  useEffect(() => {
    httpRefetchRef.current = httpQuery.refetch;
  }, [httpQuery]);

  const refetch = useCallback(async () => {
    if (convexSidebar.hasLiveSubscription) {
      await convexRefetchRef.current();
      const snapshot = await requestSidebarData(workspaceId);
      setManualSnapshot({ workspaceId, data: snapshot });
      return snapshot;
    }
    if (shouldUseHttpFallback) {
      return httpRefetchRef.current();
    }
    return liveDataRef.current;
  }, [
    convexSidebar.hasLiveSubscription,
    shouldUseHttpFallback,
    workspaceId,
  ]);
  const stableLiveData = getStableSidebarData(liveDataSyncKey, liveData);

  return useMemo<SidebarDataResult>(() => {
    return {
      data: stableLiveData,
      isLoading,
      error,
      refetch,
      source,
    };
  }, [
    error,
    isLoading,
    refetch,
    stableLiveData,
    source,
  ]);
}
