import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
import { useNodesState, useEdgesState, addEdge, Connection, Edge, NodeChange } from '@xyflow/react';
import { useUndoRedoWithState } from '../../hooks/useUndoRedo';
import { useWorkflowPersistence } from '../../hooks/useWorkflowPersistence';
import { useCanvasActions } from '../../hooks/useCanvasActions';
import { useNodeCallbacks } from '../../hooks/useNodeCallbacks';
import { NameSaveStatus } from './Toolbar';
import { renameWorkflow } from '../../services/workflows';
import {
  WorkflowReactFlowNode as WorkflowReactFlowNodeType,
  WorkflowNodeData,
} from '../../types/reactflow';
import { Version } from '../../types/workflow';
import { getConfigSummary } from '../../utils/display';
import { calculateStepNumbers } from '../../utils/transforms';
import {
  useWorkflowStore,
  useUIStore,
  useWorkflowName,
  useIsActive,
  useIsSaving,
  useIsActivating,
  useIsPublishing,
  usePublishedVersionId,
  useHasUnpublishedChanges,
  useWorkflowActions,
  useWorkflowSetters,
  useAppSelector,
  useDataFieldsModal,
  useDialogState,
  useVersionStore,
  useVersionHistory,
  useVersionActions,
} from '../../stores';
import { toast } from '../../lib/toast';
import { __ } from '../../lib/i18n';

interface UseFlowBuilderStateOptions {
  workflowId?: string;
  onBack: () => void;
  onWorkflowCreated?: (id: number) => void;
}

/**
 * Hook that encapsulates all FlowBuilder state, effects, and callbacks.
 *
 * This separates business logic from rendering, making the FlowBuilder
 * component a thin composition shell.
 */
export function useFlowBuilderState({
  workflowId,
  onBack,
  onWorkflowCreated,
}: UseFlowBuilderStateOptions) {
  // ---------------------------------------------------------------------------
  // Store selectors
  // ---------------------------------------------------------------------------

  const workflowName = useWorkflowName();
  const isActive = useIsActive();
  const isSaving = useIsSaving();
  const isActivating = useIsActivating();
  const isPublishing = useIsPublishing();
  const publishedVersionId = usePublishedVersionId();
  const hasUnpublishedChanges = useHasUnpublishedChanges();

  const {
    setName: setWorkflowName,
    activate,
    deactivate,
    hasNameChanged,
    publish,
    refreshVersionStatus,
    markAsUnpublished,
  } = useWorkflowActions();
  const { setInitialName } = useWorkflowSetters();

  const { openHistoryPanel, closeHistoryPanel } = useVersionHistory();
  const { isRestoring, restoreVersion, makeVersionLive } = useVersionActions();

  const { openAppSelector, closeAppSelector } = useAppSelector();
  const { openDataFieldsModal } = useDataFieldsModal();
  const { setShowUnsavedDialog } = useDialogState();

  // ---------------------------------------------------------------------------
  // Local state
  // ---------------------------------------------------------------------------

  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
  const [nameSaveStatus, setNameSaveStatus] = useState<NameSaveStatus>('idle');
  const [versionToRestore, setVersionToRestore] = useState<Version | null>(null);

  const justCreatedRef = useRef(false);
  const nameSaveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // ---------------------------------------------------------------------------
  // ReactFlow state
  // ---------------------------------------------------------------------------

  const [nodes, setNodes, onNodesChangeBase] = useNodesState<WorkflowReactFlowNodeType>([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);

  const onNodesChange = useCallback(
    (changes: NodeChange<WorkflowReactFlowNodeType>[]) => {
      const filteredChanges = changes.filter((change) => change.type !== 'position');
      onNodesChangeBase(filteredChanges);
    },
    [onNodesChangeBase],
  );

  // ---------------------------------------------------------------------------
  // Hooks
  // ---------------------------------------------------------------------------

  const { canUndo, canRedo, undoCount, redoCount, takeSnapshot, undo, redo, clearHistory } =
    useUndoRedoWithState(nodes, edges, setNodes, setEdges);

  const handleWorkflowCreated = useCallback(
    (id: number) => {
      justCreatedRef.current = true;
      onWorkflowCreated?.(id);
    },
    [onWorkflowCreated],
  );

  const { loading, loadError, loadWorkflow, saveWorkflow, retryLoad } = useWorkflowPersistence(
    workflowId,
    handleWorkflowCreated,
  );

  const canvasActions = useCanvasActions({
    nodes,
    edges,
    setNodes,
    setEdges,
    takeSnapshot,
    setSelectedNodeId,
  });

  const { displayNodes, addStepEntries } = useNodeCallbacks({
    nodes,
    edges,
    onEdit: setSelectedNodeId,
    onDelete: canvasActions.handleRequestDeleteNode,
    onSelectApp: (nodeId, placeholderType) => openAppSelector(placeholderType, nodeId),
    onViewDataFields: (_nodeId, nodeType, actionType) => openDataFieldsModal(nodeType, actionType),
    onAddPlaceholder: canvasActions.handleAddPlaceholder,
  });

  // ---------------------------------------------------------------------------
  // Effects
  // ---------------------------------------------------------------------------

  // Load workflow on mount (skip if we just created it)
  useEffect(() => {
    if (!workflowId) return;
    if (justCreatedRef.current) {
      justCreatedRef.current = false;
      return;
    }
    loadWorkflow(workflowId).then((result) => {
      if (result) {
        setNodes(result.nodes);
        setEdges(result.edges);
      }
    });
  }, [workflowId]); // eslint-disable-line react-hooks/exhaustive-deps

  // Refresh version status when workflow loads
  useEffect(() => {
    if (workflowId) refreshVersionStatus();
  }, [workflowId, refreshVersionStatus]);

  // Update step numbers
  useEffect(() => {
    if (nodes.length === 0) return;
    const stepNumbers = calculateStepNumbers(nodes, edges);
    let needsUpdate = false;
    nodes.forEach((node) => {
      if (node.data.stepNumber !== stepNumbers.get(node.id)) needsUpdate = true;
    });
    if (needsUpdate) {
      setNodes((nds) =>
        nds.map((node) => ({
          ...node,
          data: { ...node.data, stepNumber: stepNumbers.get(node.id) },
        })),
      );
    }
  }, [nodes.length, edges]); // eslint-disable-line react-hooks/exhaustive-deps

  // Clear selection when nodes empty
  useEffect(() => {
    if (nodes.length === 0) {
      setSelectedNodeId(null);
      closeAppSelector();
    }
  }, [nodes.length, closeAppSelector]);

  // Keyboard shortcuts
  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      const target = event.target as HTMLElement;
      if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)
        return;

      if ((event.ctrlKey || event.metaKey) && event.key === 'z' && !event.shiftKey) {
        event.preventDefault();
        if (canUndo) undo();
        return;
      }
      if (
        ((event.ctrlKey || event.metaKey) && event.key === 'y') ||
        ((event.ctrlKey || event.metaKey) &&
          event.shiftKey &&
          (event.key === 'z' || event.key === 'Z'))
      ) {
        event.preventDefault();
        if (canRedo) redo();
        return;
      }
      if ((event.key === 'Delete' || event.key === 'Backspace') && selectedNodeId) {
        event.preventDefault();
        canvasActions.handleRequestDeleteNode(selectedNodeId);
      }
    };
    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  }, [selectedNodeId, canUndo, canRedo, undo, redo, canvasActions]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (nameSaveTimeoutRef.current) clearTimeout(nameSaveTimeoutRef.current);
      useWorkflowStore.getState().reset();
      useUIStore.getState().reset();
      useVersionStore.getState().reset();
    };
  }, []);

  // ---------------------------------------------------------------------------
  // Computed values
  // ---------------------------------------------------------------------------

  const hasUnsavedChanges = useMemo(
    () => undoCount > 0 || hasNameChanged(),
    [undoCount, hasNameChanged],
  );

  const edgesWithInsert = useMemo(() => {
    const mapped: Edge[] = edges
      .filter((edge) => {
        const targetNode = nodes.find((n) => n.id === edge.target);
        return targetNode?.type !== 'trigger';
      })
      .map((edge) => ({
        ...edge,
        type: 'insertStep',
        data: { onInsertClick: undefined },
      }));

    // Add pseudo-edges from parent nodes to AddStep buttons
    addStepEntries.forEach((entry) => {
      mapped.push({
        id: `e-to-${entry.addStepId}`,
        source: entry.parentId,
        target: entry.addStepId,
        ...(entry.sourceHandle ? { sourceHandle: entry.sourceHandle } : {}),
        type: 'insertStep',
        data: { onInsertClick: undefined },
      });
    });

    return mapped;
  }, [edges, nodes, addStepEntries]);

  const selectedNode = nodes.find((n) => n.id === selectedNodeId);
  const sidebarOpen = !!selectedNode && selectedNode.data.status !== 'placeholder';

  // ---------------------------------------------------------------------------
  // Callbacks
  // ---------------------------------------------------------------------------

  const onConnect = useCallback(
    (connection: Connection) => {
      const sourceNode = nodes.find((n) => n.id === connection.source);
      const targetNode = nodes.find((n) => n.id === connection.target);
      if (!sourceNode || !targetNode) return;
      if (sourceNode.type === 'trigger' && targetNode.type === 'trigger') return;
      if (sourceNode.type === 'action' && targetNode.type === 'trigger') return;
      takeSnapshot(nodes, edges);
      setEdges((eds) => addEdge({ ...connection, type: 'smoothstep' }, eds));
    },
    [nodes, edges, setEdges, takeSnapshot],
  );

  const handleNodeClick = useCallback(
    (_event: React.MouseEvent, node: WorkflowReactFlowNodeType) => {
      if (node.data.status === 'placeholder') {
        openAppSelector(node.data.placeholderType || 'action', node.id);
      } else {
        setSelectedNodeId(node.id);
      }
    },
    [openAppSelector],
  );

  const handleBack = useCallback(() => {
    if (hasUnsavedChanges) setShowUnsavedDialog(true);
    else onBack();
  }, [hasUnsavedChanges, onBack, setShowUnsavedDialog]);

  const handleSave = useCallback(async () => {
    const success = await saveWorkflow({ nodes, edges, workflowName });
    if (success) {
      clearHistory();
      setInitialName(workflowName);
      markAsUnpublished();
      toast.success(__('Workflow saved'));
    }
  }, [nodes, edges, workflowName, saveWorkflow, clearHistory, setInitialName, markAsUnpublished]);

  const handlePublish = useCallback(async () => {
    const success = await publish();
    if (success) toast.success(__('Workflow published successfully'));
  }, [publish]);

  const handleRestoreRequest = useCallback(
    (version: Version) => {
      setVersionToRestore(version);
      closeHistoryPanel();
    },
    [closeHistoryPanel],
  );

  const handleRestoreConfirm = useCallback(async () => {
    if (!versionToRestore || !workflowId) return;
    const result = await restoreVersion(parseInt(workflowId), versionToRestore.id);
    if (result.success) {
      const loadResult = await loadWorkflow(workflowId);
      if (loadResult) {
        setNodes(loadResult.nodes);
        setEdges(loadResult.edges);
        clearHistory();
      }
      toast.success(__('Version restored. Publish to make it live.'));
      setVersionToRestore(null);
      refreshVersionStatus();
    }
  }, [
    versionToRestore,
    workflowId,
    restoreVersion,
    loadWorkflow,
    setNodes,
    setEdges,
    clearHistory,
    refreshVersionStatus,
  ]);

  const handleMakeLive = useCallback(
    async (version: Version) => {
      if (!workflowId) return;
      const result = await makeVersionLive(parseInt(workflowId), version.id);
      if (result.success) {
        const loadResult = await loadWorkflow(workflowId);
        if (loadResult) {
          setNodes(loadResult.nodes);
          setEdges(loadResult.edges);
          clearHistory();
        }
        toast.success(__('Version is now live'));
        closeHistoryPanel();
        refreshVersionStatus();
      }
    },
    [
      workflowId,
      makeVersionLive,
      loadWorkflow,
      setNodes,
      setEdges,
      clearHistory,
      closeHistoryPanel,
      refreshVersionStatus,
    ],
  );

  const handleNameBlur = useCallback(async () => {
    if (!workflowId || !hasNameChanged()) return;
    setNameSaveStatus('saving');
    try {
      await renameWorkflow(parseInt(workflowId), workflowName);
      setInitialName(workflowName);
      setNameSaveStatus('saved');
      nameSaveTimeoutRef.current = setTimeout(() => setNameSaveStatus('idle'), 1500);
    } catch {
      setNameSaveStatus('idle');
    }
  }, [workflowId, workflowName, hasNameChanged, setInitialName]);

  const handleToggleActive = useCallback(
    async (checked: boolean) => {
      if (isSaving || isActivating || !workflowId) return;
      if (checked) await activate();
      else await deactivate();
    },
    [isSaving, isActivating, workflowId, activate, deactivate],
  );

  const handleUpdateNode = useCallback(
    (updatedNodeData: Partial<WorkflowNodeData>) => {
      if (!selectedNodeId) return;
      takeSnapshot(nodes, edges);
      setNodes((nds) =>
        nds.map((n) => {
          if (n.id === selectedNodeId) {
            const newConfig = updatedNodeData.config || n.data.config;
            const actionType = n.data.actionType;
            const configSummary =
              actionType && newConfig
                ? getConfigSummary(newConfig as Record<string, unknown>, actionType)
                : undefined;
            return { ...n, data: { ...n.data, ...updatedNodeData, configSummary } };
          }
          return n;
        }),
      );
    },
    [selectedNodeId, nodes, edges, takeSnapshot, setNodes],
  );

  // ---------------------------------------------------------------------------
  // Return
  // ---------------------------------------------------------------------------

  return {
    // State
    nodes,
    edges,
    selectedNodeId,
    nameSaveStatus,
    versionToRestore,
    loading,
    loadError,
    displayNodes,
    edgesWithInsert,
    hasUnsavedChanges,
    sidebarOpen,

    // Store values
    workflowName,
    isActive,
    isSaving,
    isActivating,
    isPublishing,
    publishedVersionId,
    hasUnpublishedChanges,
    isRestoring,

    // Store actions
    setWorkflowName,
    openHistoryPanel,

    // Undo/Redo
    canUndo,
    canRedo,
    undoCount,
    redoCount,
    undo,
    redo,

    // Change handlers
    onNodesChange,
    onEdgesChange,
    onConnect,

    // Event handlers
    handleNodeClick,
    handleBack,
    handleSave,
    handlePublish,
    handleRestoreRequest,
    handleRestoreConfirm,
    handleMakeLive,
    handleNameBlur,
    handleToggleActive,
    handleUpdateNode,

    // Setters
    setSelectedNodeId,
    setVersionToRestore,

    // Canvas actions
    canvasActions,

    // Persistence
    retryLoad,

    // Callbacks for FlowBuilder to pass through
    onBack,
  };
}
