import {
  startTransition,
  useDeferredValue,
  useEffect,
  useEffectEvent,
  useReducer,
  useRef,
} from 'react';
import { useResearchEventSubscriptions } from '../lib/researchEvents';
import { researchBridge } from '../lib/researchBridge';
import type {
  PipelineJob,
  WorkspaceProjection,
  WorkspaceViewKind,
} from '../types/research';

interface ProjectionState {
  projection: WorkspaceProjection | null;
  isLoading: boolean;
  isRefreshing: boolean;
  error?: string;
  jobs: Record<string, PipelineJob>;
}

type ProjectionAction =
  | { type: 'load_started'; refresh: boolean }
  | { type: 'load_succeeded'; projection: WorkspaceProjection }
  | { type: 'load_failed'; error: string }
  | { type: 'job_updated'; job: PipelineJob };

const createProjectionState = (): ProjectionState => ({
  projection: null,
  isLoading: true,
  isRefreshing: false,
  error: undefined,
  jobs: {},
});

const projectionReducer = (
  state: ProjectionState,
  action: ProjectionAction,
): ProjectionState => {
  switch (action.type) {
    case 'load_started':
      return {
        ...state,
        isLoading: !action.refresh,
        isRefreshing: action.refresh,
        error: undefined,
      };
    case 'load_succeeded':
      return {
        ...state,
        projection: action.projection,
        isLoading: false,
        isRefreshing: false,
        error: undefined,
      };
    case 'load_failed':
      return {
        ...state,
        isLoading: false,
        isRefreshing: false,
        error: action.error,
      };
    case 'job_updated':
      return {
        ...state,
        jobs: {
          ...state.jobs,
          [action.job.id]: action.job,
        },
      };
    default:
      return state;
  }
};

export const useResearchProjection = (
  workspaceId: string | null,
  view: WorkspaceViewKind,
) => {
  const [state, dispatch] = useReducer(projectionReducer, undefined, createProjectionState);
  const deferredView = useDeferredValue(view);
  const refreshTimeoutRef = useRef<number | null>(null);

  const loadProjection = useEffectEvent(async (refresh: boolean) => {
    if (!workspaceId) {
      return;
    }

    dispatch({ type: 'load_started', refresh });
    try {
      const projection = await researchBridge.getWorkspaceProjection({
        workspaceId,
        view: deferredView,
      });
      dispatch({ type: 'load_succeeded', projection });
    } catch (loadError) {
      dispatch({
        type: 'load_failed',
        error: loadError instanceof Error ? loadError.message : String(loadError),
      });
    }
  });

  useEffect(() => {
    if (!workspaceId) {
      return;
    }
    void loadProjection(false);
  }, [deferredView, loadProjection, workspaceId]);

  useEffect(() => {
    return () => {
      if (refreshTimeoutRef.current != null) {
        window.clearTimeout(refreshTimeoutRef.current);
      }
    };
  }, []);

  const scheduleRefresh = useEffectEvent(() => {
    if (refreshTimeoutRef.current != null) {
      window.clearTimeout(refreshTimeoutRef.current);
    }

    refreshTimeoutRef.current = window.setTimeout(() => {
      void loadProjection(true);
      refreshTimeoutRef.current = null;
    }, 150);
  });

  useResearchEventSubscriptions({
    workspaceId: workspaceId ?? undefined,
    onWorkspaceUpdate: () => {
      scheduleRefresh();
    },
    onNoteUpdate: () => {
      scheduleRefresh();
    },
    onGhostUpdate: () => {
      scheduleRefresh();
    },
    onJobUpdate: (payload) => {
      startTransition(() => {
        dispatch({ type: 'job_updated', job: payload.job });
      });
    },
  });

  return {
    ...state,
    refreshProjection: () => loadProjection(true),
  };
};
