import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { toast } from '../../lib/toast';
import FlowBuilder from './index';
import {
  createMockTriggerNode,
  createMockActionNode,
  createMockEdge,
  TRIGGER_NODE_ID,
  ACTION_NODE_ID,
} from '../../test/helpers';

// ============================================================================
// Mock Setup (using vi.hoisted for variables used in mocks)
// ============================================================================

// Hoisted mock state that can be used in vi.mock
const { mockWorkflowStoreState, mockUIStoreState } = vi.hoisted(() => {
  return {
    mockWorkflowStoreState: {
      id: null as number | null,
      name: 'Untitled Workflow',
      isActive: false,
      initialName: null as string | null,
      isLoading: false,
      isSaving: false,
      isActivating: false,
      loadError: null as string | null,
      setId: vi.fn(),
      setName: vi.fn(),
      setInitialName: vi.fn(),
      setActive: vi.fn(),
      setIsSaving: vi.fn(),
      activate: vi.fn().mockResolvedValue(true),
      deactivate: vi.fn().mockResolvedValue(true),
      hasNameChanged: vi.fn().mockReturnValue(false),
      reset: vi.fn(),
      markAsUnpublished: vi.fn(),
      refreshVersionStatus: vi.fn(),
    },
    mockUIStoreState: {
      showAppSelector: false,
      appSelectorType: 'action' as const,
      selectedPlaceholderId: null as string | null,
      dataFieldsModal: null as { nodeType: 'trigger' | 'action'; typeIdentifier: string } | null,
      nodeToDelete: null as { id: string; name: string } | null,
      showUnsavedDialog: false,
      openAppSelector: vi.fn(),
      closeAppSelector: vi.fn(),
      openDataFieldsModal: vi.fn(),
      closeDataFieldsModal: vi.fn(),
      requestDeleteNode: vi.fn(),
      cancelDeleteNode: vi.fn(),
      clearNodeToDelete: vi.fn(),
      setShowUnsavedDialog: vi.fn(),
      reset: vi.fn(),
    },
  };
});

vi.mock('../../stores', () => {
  const useWorkflowStore = Object.assign(
    (selector?: (state: typeof mockWorkflowStoreState) => unknown) => {
      if (selector) return selector(mockWorkflowStoreState);
      return mockWorkflowStoreState;
    },
    { getState: () => mockWorkflowStoreState },
  );

  const useUIStore = Object.assign(
    (selector?: (state: typeof mockUIStoreState) => unknown) => {
      if (selector) return selector(mockUIStoreState);
      return mockUIStoreState;
    },
    { getState: () => mockUIStoreState },
  );

  // Workflow state selectors
  const useWorkflowName = () => mockWorkflowStoreState.name;
  const useWorkflowId = () => mockWorkflowStoreState.id;
  const useIsActive = () => mockWorkflowStoreState.isActive;
  const useIsSaving = () => mockWorkflowStoreState.isSaving;
  const useIsActivating = () => mockWorkflowStoreState.isActivating;
  const useIsPublishing = () => false;
  const useHasUnpublishedChanges = () => false;
  const usePublishedVersionId = () => null;

  // Workflow action selectors
  const useWorkflowActions = () => ({
    setName: mockWorkflowStoreState.setName,
    setActive: mockWorkflowStoreState.setActive,
    activate: mockWorkflowStoreState.activate,
    deactivate: mockWorkflowStoreState.deactivate,
    hasNameChanged: mockWorkflowStoreState.hasNameChanged,
    publish: vi.fn().mockResolvedValue(true),
    refreshVersionStatus: mockWorkflowStoreState.refreshVersionStatus,
    markAsUnpublished: mockWorkflowStoreState.markAsUnpublished,
  });

  const useWorkflowSetters = () => ({
    setId: mockWorkflowStoreState.setId,
    setName: mockWorkflowStoreState.setName,
    setActive: mockWorkflowStoreState.setActive,
    setIsSaving: mockWorkflowStoreState.setIsSaving,
    setInitialName: mockWorkflowStoreState.setInitialName,
  });

  // UI selectors
  const useAppSelector = () => ({
    showAppSelector: mockUIStoreState.showAppSelector,
    appSelectorType: mockUIStoreState.appSelectorType,
    selectedPlaceholderId: mockUIStoreState.selectedPlaceholderId,
    openAppSelector: mockUIStoreState.openAppSelector,
    closeAppSelector: mockUIStoreState.closeAppSelector,
  });

  const useDialogState = () => ({
    nodeToDelete: mockUIStoreState.nodeToDelete,
    showUnsavedDialog: mockUIStoreState.showUnsavedDialog,
    setShowUnsavedDialog: mockUIStoreState.setShowUnsavedDialog,
    requestDeleteNode: mockUIStoreState.requestDeleteNode,
    cancelDeleteNode: mockUIStoreState.cancelDeleteNode,
    clearNodeToDelete: mockUIStoreState.clearNodeToDelete,
  });

  const useDataFieldsModal = () => ({
    dataFieldsModal: mockUIStoreState.dataFieldsModal,
    openDataFieldsModal: mockUIStoreState.openDataFieldsModal,
    closeDataFieldsModal: mockUIStoreState.closeDataFieldsModal,
  });

  // Version selectors
  const mockVersionStoreState = {
    isHistoryPanelOpen: false,
    openHistoryPanel: vi.fn(),
    closeHistoryPanel: vi.fn(),
    isRestoring: false,
    restoreVersion: vi.fn().mockResolvedValue({ success: true }),
    makeVersionLive: vi.fn().mockResolvedValue({ success: true }),
    isMakingLive: false,
    reset: vi.fn(),
  };

  const useVersionStore = Object.assign(
    (selector?: (state: typeof mockVersionStoreState) => unknown) => {
      if (selector) return selector(mockVersionStoreState);
      return mockVersionStoreState;
    },
    { getState: () => mockVersionStoreState },
  );

  const useVersionHistory = () => ({
    isHistoryPanelOpen: mockVersionStoreState.isHistoryPanelOpen,
    openHistoryPanel: mockVersionStoreState.openHistoryPanel,
    closeHistoryPanel: mockVersionStoreState.closeHistoryPanel,
  });

  const useVersionActions = () => ({
    loadVersions: vi.fn(),
    restoreVersion: mockVersionStoreState.restoreVersion,
    makeVersionLive: mockVersionStoreState.makeVersionLive,
    isRestoring: mockVersionStoreState.isRestoring,
    isMakingLive: mockVersionStoreState.isMakingLive,
  });

  return {
    useWorkflowStore,
    useUIStore,
    useVersionStore,
    useWorkflowName,
    useWorkflowId,
    useIsActive,
    useIsSaving,
    useIsActivating,
    useIsPublishing,
    useHasUnpublishedChanges,
    usePublishedVersionId,
    useWorkflowActions,
    useWorkflowSetters,
    useAppSelector,
    useDialogState,
    useDataFieldsModal,
    useVersionHistory,
    useVersionActions,
  };
});

// Mock custom toast
vi.mock('../../lib/toast', () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
    info: vi.fn(),
  },
}));

// Mock hooks
const mockLoadWorkflow = vi.fn();
const mockSaveWorkflow = vi.fn();
const mockRetryLoad = vi.fn();

const mockClearLoadError = vi.fn();

vi.mock('../../hooks/useWorkflowPersistence', () => ({
  useWorkflowPersistence: vi.fn(() => ({
    loading: false,
    loadError: null,
    loadWorkflow: mockLoadWorkflow,
    saveWorkflow: mockSaveWorkflow,
    retryLoad: mockRetryLoad,
    clearLoadError: mockClearLoadError,
  })),
}));

const mockHandleAddPlaceholder = vi.fn();
const mockHandleRequestDeleteNode = vi.fn();
const mockHandleConfirmDeleteNode = vi.fn();
const mockHandleCancelDeleteNode = vi.fn();
const mockHandleAppSelected = vi.fn();
const mockHandleCloseAppSelector = vi.fn();

vi.mock('../../hooks/useCanvasActions', () => ({
  useCanvasActions: vi.fn(() => ({
    handleAddPlaceholder: mockHandleAddPlaceholder,
    handleRequestDeleteNode: mockHandleRequestDeleteNode,
    handleConfirmDeleteNode: mockHandleConfirmDeleteNode,
    handleCancelDeleteNode: mockHandleCancelDeleteNode,
    handleAppSelected: mockHandleAppSelected,
    handleCloseAppSelector: mockHandleCloseAppSelector,
  })),
}));

const mockDisplayNodes: ReturnType<typeof createMockTriggerNode>[] = [];
vi.mock('../../hooks/useNodeCallbacks', () => ({
  useNodeCallbacks: vi.fn(() => ({
    displayNodes: mockDisplayNodes,
    addStepEntries: [],
  })),
}));

const mockUndo = vi.fn();
const mockRedo = vi.fn();
const mockTakeSnapshot = vi.fn();
const mockClearHistory = vi.fn();
let mockCanUndo = false;
let mockCanRedo = false;
let mockUndoCount = 0;
let mockRedoCount = 0;

vi.mock('../../hooks/useUndoRedo', () => ({
  useUndoRedoWithState: vi.fn(() => ({
    canUndo: mockCanUndo,
    canRedo: mockCanRedo,
    undoCount: mockUndoCount,
    redoCount: mockRedoCount,
    takeSnapshot: mockTakeSnapshot,
    undo: mockUndo,
    redo: mockRedo,
    clearHistory: mockClearHistory,
  })),
}));

vi.mock('../../hooks/useViewportController', () => ({
  ViewportController: () => null,
}));

// Mock services
vi.mock('../../services/workflows', () => ({
  renameWorkflow: vi.fn().mockResolvedValue({}),
}));

// Mock child components
vi.mock('../ConfigPanel', () => ({
  ConfigPanel: ({ node, onClose }: { node: { id: string }; onClose: () => void }) => (
    <div data-testid="config-panel" data-node-id={node.id}>
      <button onClick={onClose}>Close Panel</button>
    </div>
  ),
}));

vi.mock('../AppSelectorPanel', () => ({
  AppSelectorPanel: ({ onClose }: { onClose: () => void }) => (
    <div data-testid="app-selector-panel">
      <button onClick={onClose}>Close Selector</button>
    </div>
  ),
}));

vi.mock('../DataFieldsModal', () => ({
  DataFieldsModal: () => <div data-testid="data-fields-modal" />,
}));

vi.mock('./Toolbar', () => ({
  Toolbar: ({
    workflowName,
    onNameChange,
    onBack,
    onSave,
    onUndo,
    onRedo,
    canUndo,
    canRedo,
    isSaving,
    hasUnsavedChanges,
    isActive,
    onToggleActive,
  }: {
    workflowName: string;
    onNameChange: (name: string) => void;
    onBack: () => void;
    onSave: () => void;
    onUndo: () => void;
    onRedo: () => void;
    canUndo: boolean;
    canRedo: boolean;
    isSaving: boolean;
    hasUnsavedChanges: boolean;
    isActive: boolean;
    onToggleActive: (active: boolean) => void;
  }) => (
    <div data-testid="toolbar">
      <input
        data-testid="workflow-name-input"
        value={workflowName}
        onChange={(e) => onNameChange(e.target.value)}
      />
      <button data-testid="back-button" onClick={onBack}>
        Back
      </button>
      <button data-testid="save-button" onClick={onSave} disabled={isSaving}>
        Save
      </button>
      <button data-testid="undo-button" onClick={onUndo} disabled={!canUndo}>
        Undo
      </button>
      <button data-testid="redo-button" onClick={onRedo} disabled={!canRedo}>
        Redo
      </button>
      <input
        data-testid="active-toggle"
        type="checkbox"
        checked={isActive}
        onChange={(e) => onToggleActive(e.target.checked)}
      />
      <span data-testid="unsaved-indicator">{hasUnsavedChanges ? 'Unsaved' : 'Saved'}</span>
    </div>
  ),
  NameSaveStatus: 'idle',
}));

vi.mock('./CanvasOverlays', () => ({
  CanvasOverlays: ({
    loading,
    error,
    loadError,
    onAddTrigger,
    onRetryLoad,
  }: {
    loading: boolean;
    error: string[] | null;
    loadError: string | null;
    onAddTrigger: () => void;
    onRetryLoad: () => void;
  }) => (
    <div data-testid="canvas-overlays">
      {loading && <div data-testid="loading-overlay">Loading...</div>}
      {error && <div data-testid="error-overlay">{error.join(', ')}</div>}
      {loadError && (
        <div data-testid="load-error-overlay">
          {loadError}
          <button data-testid="retry-load-button" onClick={onRetryLoad}>
            Retry
          </button>
        </div>
      )}
      <button data-testid="add-trigger-button" onClick={onAddTrigger}>
        Add Trigger
      </button>
    </div>
  ),
}));

vi.mock('./Dialogs', () => ({
  Dialogs: ({
    onConfirmDeleteNode,
    onCancelDeleteNode,
    onLeaveWithoutSaving,
  }: {
    onConfirmDeleteNode: () => void;
    onCancelDeleteNode: () => void;
    onLeaveWithoutSaving: () => void;
  }) => (
    <div data-testid="dialogs">
      <button data-testid="confirm-delete-button" onClick={onConfirmDeleteNode}>
        Confirm Delete
      </button>
      <button data-testid="cancel-delete-button" onClick={onCancelDeleteNode}>
        Cancel Delete
      </button>
      <button data-testid="leave-without-saving-button" onClick={onLeaveWithoutSaving}>
        Leave Without Saving
      </button>
    </div>
  ),
}));

// ============================================================================
// Test Setup & Teardown
// ============================================================================

describe('FlowBuilder', () => {
  const defaultProps = {
    onBack: vi.fn(),
    onWorkflowCreated: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();

    // Reset store mocks to default values
    Object.assign(mockWorkflowStoreState, {
      id: null,
      name: 'Untitled Workflow',
      isActive: false,
      initialName: null,
      isLoading: false,
      isSaving: false,
      isActivating: false,
      loadError: null,
      setId: vi.fn(),
      setName: vi.fn(),
      setInitialName: vi.fn(),
      setActive: vi.fn(),
      setIsSaving: vi.fn(),
      activate: vi.fn().mockResolvedValue(true),
      deactivate: vi.fn().mockResolvedValue(true),
      hasNameChanged: vi.fn().mockReturnValue(false),
      reset: vi.fn(),
    });

    Object.assign(mockUIStoreState, {
      showAppSelector: false,
      appSelectorType: 'action' as const,
      selectedPlaceholderId: null,
      dataFieldsModal: null,
      nodeToDelete: null,
      showUnsavedDialog: false,
      openAppSelector: vi.fn(),
      closeAppSelector: vi.fn(),
      openDataFieldsModal: vi.fn(),
      closeDataFieldsModal: vi.fn(),
      requestDeleteNode: vi.fn(),
      cancelDeleteNode: vi.fn(),
      clearNodeToDelete: vi.fn(),
      setShowUnsavedDialog: vi.fn(),
      reset: vi.fn(),
    });

    // Reset hook state
    mockCanUndo = false;
    mockCanRedo = false;
    mockUndoCount = 0;
    mockRedoCount = 0;

    // Reset displayNodes
    mockDisplayNodes.length = 0;

    // Default successful load
    mockLoadWorkflow.mockResolvedValue({
      nodes: [createMockTriggerNode(), createMockActionNode()],
      edges: [createMockEdge(TRIGGER_NODE_ID, ACTION_NODE_ID)],
    });

    mockSaveWorkflow.mockResolvedValue(true);
  });

  afterEach(() => {
    vi.clearAllTimers();
  });

  // ==========================================================================
  // Rendering Tests
  // ==========================================================================

  describe('rendering', () => {
    it('renders the toolbar', () => {
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('toolbar')).toBeInTheDocument();
    });

    it('renders canvas overlays', () => {
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('canvas-overlays')).toBeInTheDocument();
    });

    it('renders dialogs component', () => {
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('dialogs')).toBeInTheDocument();
    });

    it('renders data fields modal', () => {
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('data-fields-modal')).toBeInTheDocument();
    });
  });

  // ==========================================================================
  // Workflow Loading Tests
  // ==========================================================================

  describe('workflow loading', () => {
    it('loads workflow when workflowId is provided', async () => {
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await waitFor(() => {
        expect(mockLoadWorkflow).toHaveBeenCalledWith('123');
      });
    });

    it('does not load when workflowId is not provided', () => {
      render(<FlowBuilder {...defaultProps} />);
      expect(mockLoadWorkflow).not.toHaveBeenCalled();
    });

    it('shows loading overlay while loading', async () => {
      const { useWorkflowPersistence } = await import('../../hooks/useWorkflowPersistence');
      vi.mocked(useWorkflowPersistence).mockReturnValue({
        loading: true,
        loadError: null,
        loadWorkflow: mockLoadWorkflow,
        saveWorkflow: mockSaveWorkflow,
        retryLoad: mockRetryLoad,
        clearLoadError: mockClearLoadError,
      });

      render(<FlowBuilder {...defaultProps} workflowId="123" />);
      expect(screen.getByTestId('loading-overlay')).toBeInTheDocument();
    });

    it('shows load error with retry button', async () => {
      const { useWorkflowPersistence } = await import('../../hooks/useWorkflowPersistence');
      vi.mocked(useWorkflowPersistence).mockReturnValue({
        loading: false,
        loadError: 'Failed to load workflow',
        loadWorkflow: mockLoadWorkflow,
        saveWorkflow: mockSaveWorkflow,
        retryLoad: mockRetryLoad,
        clearLoadError: mockClearLoadError,
      });

      render(<FlowBuilder {...defaultProps} workflowId="123" />);
      expect(screen.getByTestId('load-error-overlay')).toBeInTheDocument();
      expect(screen.getByText('Failed to load workflow')).toBeInTheDocument();
    });

    it('retries load when retry button is clicked', async () => {
      const { useWorkflowPersistence } = await import('../../hooks/useWorkflowPersistence');
      vi.mocked(useWorkflowPersistence).mockReturnValue({
        loading: false,
        loadError: 'Failed to load workflow',
        loadWorkflow: mockLoadWorkflow,
        saveWorkflow: mockSaveWorkflow,
        retryLoad: mockRetryLoad,
        clearLoadError: mockClearLoadError,
      });

      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('retry-load-button'));
      expect(mockRetryLoad).toHaveBeenCalledWith('123');
    });

    it('calls onWorkflowCreated callback when workflow is created', async () => {
      const onWorkflowCreated = vi.fn();

      // The useWorkflowPersistence hook receives onWorkflowCreated and calls it
      const { useWorkflowPersistence } = await import('../../hooks/useWorkflowPersistence');
      vi.mocked(useWorkflowPersistence).mockImplementation((_workflowId, onCreated) => {
        return {
          loading: false,
          loadError: null,
          loadWorkflow: mockLoadWorkflow.mockImplementation(async () => {
            // Simulate successful load
            return {
              nodes: [createMockTriggerNode()],
              edges: [],
            };
          }),
          saveWorkflow: mockSaveWorkflow.mockImplementation(async () => {
            // Simulate workflow creation triggering callback
            if (onCreated) onCreated(999);
            return true;
          }),
          retryLoad: mockRetryLoad,
          clearLoadError: mockClearLoadError,
        };
      });

      render(<FlowBuilder {...defaultProps} onWorkflowCreated={onWorkflowCreated} />);

      // Save workflow to trigger creation
      await userEvent.click(screen.getByTestId('save-button'));

      await waitFor(() => {
        expect(onWorkflowCreated).toHaveBeenCalledWith(999);
      });
    });
  });

  // ==========================================================================
  // Save Tests
  // ==========================================================================

  describe('saving', () => {
    it('saves workflow when save button is clicked', async () => {
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('save-button'));

      expect(mockSaveWorkflow).toHaveBeenCalled();
    });

    it('clears history after successful save', async () => {
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('save-button'));

      await waitFor(() => {
        expect(mockClearHistory).toHaveBeenCalled();
      });
    });

    it('updates initial name after successful save', async () => {
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('save-button'));

      await waitFor(() => {
        expect(mockWorkflowStoreState.setInitialName).toHaveBeenCalled();
      });
    });

    it('shows saving state in toolbar', async () => {
      Object.assign(mockWorkflowStoreState, { isSaving: true });

      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      expect(screen.getByTestId('save-button')).toBeDisabled();
    });

    it('shows success toast when workflow is saved', async () => {
      mockSaveWorkflow.mockResolvedValue(true);
      vi.mocked(toast.success).mockClear();

      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('save-button'));

      await waitFor(() => {
        expect(toast.success).toHaveBeenCalledWith('Workflow saved');
      });
    });
  });

  // ==========================================================================
  // Undo/Redo Tests
  // ==========================================================================

  describe('undo/redo', () => {
    it('undo button is disabled when canUndo is false', () => {
      mockCanUndo = false;
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('undo-button')).toBeDisabled();
    });

    it('undo button is enabled when canUndo is true', () => {
      mockCanUndo = true;
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('undo-button')).not.toBeDisabled();
    });

    it('redo button is disabled when canRedo is false', () => {
      mockCanRedo = false;
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('redo-button')).toBeDisabled();
    });

    it('redo button is enabled when canRedo is true', () => {
      mockCanRedo = true;
      render(<FlowBuilder {...defaultProps} />);
      expect(screen.getByTestId('redo-button')).not.toBeDisabled();
    });

    it('calls undo when undo button is clicked', async () => {
      mockCanUndo = true;
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('undo-button'));
      expect(mockUndo).toHaveBeenCalled();
    });

    it('calls redo when redo button is clicked', async () => {
      mockCanRedo = true;
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('redo-button'));
      expect(mockRedo).toHaveBeenCalled();
    });

    it('calls undo on Ctrl+Z keyboard shortcut', async () => {
      mockCanUndo = true;
      render(<FlowBuilder {...defaultProps} />);

      await act(async () => {
        fireEvent.keyDown(document, { key: 'z', ctrlKey: true });
      });

      expect(mockUndo).toHaveBeenCalled();
    });

    it('calls redo on Ctrl+Y keyboard shortcut', async () => {
      mockCanRedo = true;
      render(<FlowBuilder {...defaultProps} />);

      await act(async () => {
        fireEvent.keyDown(document, { key: 'y', ctrlKey: true });
      });

      expect(mockRedo).toHaveBeenCalled();
    });

    it('calls redo on Ctrl+Shift+Z keyboard shortcut', async () => {
      mockCanRedo = true;
      render(<FlowBuilder {...defaultProps} />);

      await act(async () => {
        fireEvent.keyDown(document, { key: 'z', ctrlKey: true, shiftKey: true });
      });

      expect(mockRedo).toHaveBeenCalled();
    });

    it('ignores keyboard shortcuts when typing in input', async () => {
      mockCanUndo = true;
      render(<FlowBuilder {...defaultProps} />);

      const input = screen.getByTestId('workflow-name-input');
      input.focus();

      await act(async () => {
        fireEvent.keyDown(input, { key: 'z', ctrlKey: true });
      });

      expect(mockUndo).not.toHaveBeenCalled();
    });
  });

  // ==========================================================================
  // Node Deletion Tests
  // ==========================================================================

  describe('node deletion', () => {
    it('confirms node deletion when confirm button is clicked', async () => {
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('confirm-delete-button'));
      expect(mockHandleConfirmDeleteNode).toHaveBeenCalled();
    });

    it('cancels node deletion when cancel button is clicked', async () => {
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('cancel-delete-button'));
      expect(mockHandleCancelDeleteNode).toHaveBeenCalled();
    });
  });

  // ==========================================================================
  // Back Navigation Tests
  // ==========================================================================

  describe('back navigation', () => {
    it('shows unsaved dialog when back clicked with unsaved changes', async () => {
      mockUndoCount = 1; // Simulate unsaved changes
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('back-button'));

      expect(mockUIStoreState.setShowUnsavedDialog).toHaveBeenCalledWith(true);
    });

    it('navigates back directly when no unsaved changes', async () => {
      mockUndoCount = 0;
      mockWorkflowStoreState.hasNameChanged = vi.fn().mockReturnValue(false);

      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('back-button'));

      expect(defaultProps.onBack).toHaveBeenCalled();
    });

    it('leaves without saving when dialog confirmed', async () => {
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('leave-without-saving-button'));

      expect(defaultProps.onBack).toHaveBeenCalled();
    });
  });

  // ==========================================================================
  // Activate/Deactivate Tests
  // ==========================================================================

  describe('activate/deactivate', () => {
    it('activates workflow when toggle is turned on', async () => {
      Object.assign(mockWorkflowStoreState, { isActive: false });
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('active-toggle'));

      expect(mockWorkflowStoreState.activate).toHaveBeenCalled();
    });

    it('deactivates workflow when toggle is turned off', async () => {
      Object.assign(mockWorkflowStoreState, { isActive: true });
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('active-toggle'));

      expect(mockWorkflowStoreState.deactivate).toHaveBeenCalled();
    });

    it('does not toggle when no workflowId', async () => {
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('active-toggle'));

      expect(mockWorkflowStoreState.activate).not.toHaveBeenCalled();
      expect(mockWorkflowStoreState.deactivate).not.toHaveBeenCalled();
    });

    it('does not toggle while saving', async () => {
      Object.assign(mockWorkflowStoreState, { isSaving: true });
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('active-toggle'));

      expect(mockWorkflowStoreState.activate).not.toHaveBeenCalled();
    });

    it('does not toggle while activating', async () => {
      Object.assign(mockWorkflowStoreState, { isActivating: true });
      render(<FlowBuilder {...defaultProps} workflowId="123" />);

      await userEvent.click(screen.getByTestId('active-toggle'));

      expect(mockWorkflowStoreState.activate).not.toHaveBeenCalled();
    });
  });

  // ==========================================================================
  // Add Trigger Tests
  // ==========================================================================

  describe('add trigger', () => {
    it('adds trigger placeholder when add trigger button is clicked', async () => {
      render(<FlowBuilder {...defaultProps} />);

      await userEvent.click(screen.getByTestId('add-trigger-button'));

      expect(mockHandleAddPlaceholder).toHaveBeenCalledWith('trigger');
    });
  });

  // ==========================================================================
  // App Selector Tests
  // ==========================================================================

  describe('app selector', () => {
    it('shows app selector when showAppSelector is true', () => {
      Object.assign(mockUIStoreState, { showAppSelector: true });
      render(<FlowBuilder {...defaultProps} />);

      expect(screen.getByTestId('app-selector-panel')).toBeInTheDocument();
    });

    it('hides app selector when showAppSelector is false', () => {
      Object.assign(mockUIStoreState, { showAppSelector: false });
      render(<FlowBuilder {...defaultProps} />);

      expect(screen.queryByTestId('app-selector-panel')).not.toBeInTheDocument();
    });
  });

  // ==========================================================================
  // Unsaved Changes Indicator Tests
  // ==========================================================================

  describe('unsaved changes indicator', () => {
    it('shows unsaved when undoCount > 0', () => {
      mockUndoCount = 1;
      render(<FlowBuilder {...defaultProps} />);

      expect(screen.getByTestId('unsaved-indicator')).toHaveTextContent('Unsaved');
    });

    it('shows saved when no changes', () => {
      mockUndoCount = 0;
      mockWorkflowStoreState.hasNameChanged = vi.fn().mockReturnValue(false);
      render(<FlowBuilder {...defaultProps} />);

      expect(screen.getByTestId('unsaved-indicator')).toHaveTextContent('Saved');
    });

    it('shows unsaved when name has changed', () => {
      mockUndoCount = 0;
      mockWorkflowStoreState.hasNameChanged = vi.fn().mockReturnValue(true);
      render(<FlowBuilder {...defaultProps} />);

      expect(screen.getByTestId('unsaved-indicator')).toHaveTextContent('Unsaved');
    });
  });

  // ==========================================================================
  // Store Cleanup Tests
  // ==========================================================================

  describe('store cleanup', () => {
    it('resets workflow store on unmount', () => {
      const { unmount } = render(<FlowBuilder {...defaultProps} />);
      unmount();

      expect(mockWorkflowStoreState.reset).toHaveBeenCalled();
    });

    it('resets UI store on unmount', () => {
      const { unmount } = render(<FlowBuilder {...defaultProps} />);
      unmount();

      expect(mockUIStoreState.reset).toHaveBeenCalled();
    });
  });
});
