"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import type { SidebarInitialData } from "@/components/sidebar/types";
import {
  buildWorkspaceTreeStreamUrl,
  normalizeTreeStreamSnapshot,
  parseTreeStreamMessage,
} from "@/lib/tree-stream/protocol";
import { applyTreeStreamDelta, type TreeStreamDeltaEvent } from "@/lib/tree-stream/tree-delta";

export interface SidebarTreeStreamState {
  data: SidebarInitialData | null;
  status: "idle" | "connecting" | "live" | "fallback";
  cursor: string | null;
  error: Error | null;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

function normalizeDeltaEvent(input: unknown): TreeStreamDeltaEvent | null {
  if (!isRecord(input) || typeof input.op !== "string") {
    return null;
  }
  return {
    op: input.op as TreeStreamDeltaEvent["op"],
    node: isRecord(input.node) ? (input.node as TreeStreamDeltaEvent["node"]) : null,
    document: isRecord(input.document) ? (input.document as TreeStreamDeltaEvent["document"]) : null,
    documentId: typeof input.documentId === "string" ? input.documentId : null,
    documents: Array.isArray(input.documents) ? (input.documents as TreeStreamDeltaEvent["documents"]) : null,
    sidebar: isRecord(input.sidebar) ? input.sidebar : null,
  };
}

export function useSidebarTreeStream(initialData: SidebarInitialData): SidebarTreeStreamState {
  const workspaceId = initialData.activeWorkspaceId;
  const streamEnabled = Boolean(workspaceId);
  const cursorRef = useRef<string | null>(null);

  const [state, setState] = useState<SidebarTreeStreamState>({
    data: null,
    status: streamEnabled ? "connecting" : "idle",
    cursor: null,
    error: null,
  });
  const eventSourceRef = useRef<EventSource | null>(null);

  useEffect(() => {
    cursorRef.current = state.cursor;
  }, [state.cursor]);

  useEffect(() => {
    if (!streamEnabled) {
      cursorRef.current = null;
      setState({
        data: null,
        status: "idle",
        cursor: null,
        error: null,
      });
      return undefined;
    }

    let cancelled = false;
    let eventSource: EventSource | null = null;

    const handleMessage = (event: MessageEvent<string>) => {
      const envelope = parseTreeStreamMessage({
        rawData: event.data,
        eventType: event.type,
      });
      if (!envelope) {
        return;
      }

      setState((previous) => {
        const nextCursor = envelope.cursor ?? previous.cursor;

        if (envelope.kind === "snapshot" || envelope.kind === "resync") {
          const snapshot = normalizeTreeStreamSnapshot(envelope.data);
          if (!snapshot) {
            return {
              ...previous,
              cursor: nextCursor,
            };
          }
          return {
            data: snapshot,
            status: "live",
            cursor: nextCursor,
            error: null,
          };
        }

        if (envelope.kind === "delta" && previous.data) {
          const deltaEvent = normalizeDeltaEvent(envelope.data);
          if (!deltaEvent) {
            return {
              ...previous,
              cursor: nextCursor,
            };
          }
          return {
            data: applyTreeStreamDelta(previous.data, deltaEvent),
            status: "live",
            cursor: nextCursor,
            error: null,
          };
        }

        return {
          ...previous,
          cursor: nextCursor,
        };
      });
    };

    const handleError = () => {
      setState((previous) => ({
        ...previous,
        status: previous.data ? "live" : "fallback",
        error: previous.error ?? new Error("tree stream 连接失败"),
      }));
      eventSource?.close();
      if (eventSourceRef.current === eventSource) {
        eventSourceRef.current = null;
      }
    };

    if (cancelled) {
      return undefined;
    }

    const url = buildWorkspaceTreeStreamUrl(workspaceId, cursorRef.current);
    eventSource = new EventSource(url, { withCredentials: true });
    eventSourceRef.current = eventSource;
    eventSource.addEventListener("snapshot", handleMessage as EventListener);
    eventSource.addEventListener("delta", handleMessage as EventListener);
    eventSource.addEventListener("resync", handleMessage as EventListener);
    eventSource.onmessage = handleMessage;
    eventSource.onerror = handleError;

    return () => {
      cancelled = true;
      eventSource?.removeEventListener("snapshot", handleMessage as EventListener);
      eventSource?.removeEventListener("delta", handleMessage as EventListener);
      eventSource?.removeEventListener("resync", handleMessage as EventListener);
      eventSource?.close();
      if (eventSourceRef.current === eventSource) {
        eventSourceRef.current = null;
      }
    };
  }, [streamEnabled, workspaceId]);

  return state;
}
