/**
 * useOraclePage — Page hydration, branch state, component projection
 * Owns the canonical page state including revision tracking and optimistic updates.
 */
import { useState, useEffect, useCallback, useRef } from 'react';
import type { CanvasPage, CanvasComponent, OracleWSMessage } from '../types/canvas';
import { fetchCanvasPage, connectPageSocket } from '../lib/oracleApiClient';

export interface OraclePageState {
  page: CanvasPage | null;
  isLoading: boolean;
  error: string | null;
  isConnected: boolean;
  // Actions
  refresh: () => Promise<void>;
  optimisticallyAppendComponent: (comp: CanvasComponent) => void;
  applyRevision: (headRevision: number, components: CanvasComponent[]) => void;
}

export function useOraclePage(pageId: string | null): OraclePageState {
  const [page, setPage] = useState<CanvasPage | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const disconnectRef = useRef<(() => void) | null>(null);

  const load = useCallback(async () => {
    if (!pageId) {
      setPage(null);
      setIsLoading(false);
      return;
    }
    setIsLoading(true);
    setError(null);
    try {
      const data = await fetchCanvasPage(pageId);
      setPage(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to load page');
    } finally {
      setIsLoading(false);
    }
  }, [pageId]);

  // Connect WebSocket
  useEffect(() => {
    if (!pageId) {
      setIsConnected(false);
      return () => undefined;
    }
    const disconnect = connectPageSocket(pageId, {
      onMessage: (msg: OracleWSMessage) => handleWSMessage(msg),
      onReconnect: () => undefined,
      onOpen: () => setIsConnected(true),
      onClose: () => setIsConnected(false),
    });
    disconnectRef.current = disconnect;
    return () => {
      disconnect();
      disconnectRef.current = null;
    };
  }, [pageId, load]); // eslint-disable-line react-hooks/exhaustive-deps

  // Handle WS messages
  function handleWSMessage(msg: OracleWSMessage) {
    if (msg.type === 'oracle.page.revision.committed') {
      const { headRevision, components } = msg.payload as {
        headRevision: number;
        components: CanvasComponent[];
      };
      applyRevision(headRevision, components);
    } else if (msg.type === 'oracle.presence.updated') {
      setPage((prev) =>
        prev
          ? {
              ...prev,
              presence: {
                activeViewers: (msg.payload.activeViewers as number) ?? prev.presence.activeViewers,
                activeEditors: (msg.payload.activeEditors as number) ?? prev.presence.activeEditors,
                lastPresenceAt: msg.timestamp,
              },
            }
          : prev,
      );
    }
  }

  const optimisticallyAppendComponent = useCallback((comp: CanvasComponent) => {
    setPage((prev) => {
      if (!prev) return prev;
      // Prevent duplicate (idempotent insert)
      if (prev.components.some((c) => c.componentId === comp.componentId)) return prev;
      return { ...prev, components: [...prev.components, comp] };
    });
  }, []);

  const applyRevision = useCallback(
    (headRevision: number, components: CanvasComponent[]) => {
      setPage((prev) =>
        prev ? { ...prev, headRevision, components } : prev,
      );
    },
    [],
  );

  // Initial load
  useEffect(() => {
    void load();
  }, [load]);

  return {
    page,
    isLoading,
    error,
    isConnected,
    refresh: load,
    optimisticallyAppendComponent,
    applyRevision,
  };
}
