import { useState, useCallback } from 'react';
import { Edge, Position } from '@xyflow/react';
import { toast } from '../lib/toast';
import { __ } from '../lib/i18n';
import { workflowService } from '../services/workflows';
import { useWorkflowSetters } from '../stores';
import { WorkflowReactFlowNode } from '../types/reactflow';
import type { ActionConfig } from '../types/workflow';
import {
  NodeConfig,
  NodePosition,
  EdgeConnection,
  WorkflowConfig,
} from '../types/workflow';
import {
  getActionDisplayName,
  getAppInfo,
  getConfigSummary,
  getActionIconName,
  getTriggerIconName,
  getTriggerDisplayName,
} from '../utils/display';
import { autoLayoutNodes } from '../utils/transforms';
import { getErrorMessage } from '../utils/errorHandling';

/**
 * Result of loading a workflow
 */
export interface LoadedWorkflow {
  nodes: WorkflowReactFlowNode[];
  edges: Edge[];
}

/**
 * Data required to save a workflow
 */
export interface SaveWorkflowData {
  nodes: WorkflowReactFlowNode[];
  edges: Edge[];
  workflowName: string;
}

/**
 * Return type for useWorkflowPersistence hook
 */
export interface UseWorkflowPersistenceReturn {
  /** Whether workflow is currently loading */
  loading: boolean;
  /** Error message if load failed */
  loadError: string | null;
  /** Load a workflow by ID */
  loadWorkflow: (workflowId: string) => Promise<LoadedWorkflow | null>;
  /** Save the current workflow */
  saveWorkflow: (data: SaveWorkflowData) => Promise<boolean>;
  /** Retry loading a failed workflow */
  retryLoad: (workflowId: string) => Promise<LoadedWorkflow | null>;
  /** Clear load error */
  clearLoadError: () => void;
}

/**
 * Custom hook for managing workflow persistence (load/save operations)
 *
 * Extracts workflow loading and saving logic from FlowBuilder component
 * to improve maintainability and enable reuse.
 *
 * @example
 * ```tsx
 * const { loading, loadError, loadWorkflow, saveWorkflow, retryLoad } = useWorkflowPersistence();
 *
 * // Load workflow on mount
 * useEffect(() => {
 *   if (workflowId) {
 *     loadWorkflow(workflowId).then(result => {
 *       if (result) {
 *         setNodes(result.nodes);
 *         setEdges(result.edges);
 *       }
 *     });
 *   }
 * }, [workflowId]);
 *
 * // Save workflow (stays on editor after save)
 * const handleSave = async () => {
 *   await saveWorkflow({ nodes, edges, workflowName });
 * };
 * ```
 */
export function useWorkflowPersistence(
  workflowId?: string,
  onWorkflowCreated?: (id: number) => void,
): UseWorkflowPersistenceReturn {
  // Initialize loading to true if we have a workflowId to load
  const [loading, setLoading] = useState(!!workflowId);
  const [loadError, setLoadError] = useState<string | null>(null);

  // Get workflow store functions
  const {
    setName: setWorkflowName,
    setActive: setIsActive,
    setIsSaving,
    setInitialName,
    setId,
    setPublishedVersionId,
    setLastPublishedAt,
    setHasUnpublishedChanges,
  } = useWorkflowSetters();

  /**
   * Parse workflow config and create ReactFlow nodes/edges
   */
  const parseWorkflowConfig = useCallback(
    (
      config: WorkflowConfig,
      layoutNodes: NodePosition[],
      layoutEdges: EdgeConnection[],
    ): LoadedWorkflow => {
      const loadedNodes: WorkflowReactFlowNode[] = [];
      const loadedEdges: Edge[] = [];

      // Create trigger node
      if (config.trigger) {
        const triggerLayoutNode = layoutNodes.find((n) => n.id === 'node-trigger');
        const appInfo = getAppInfo(config.trigger.type, config.trigger.integration);

        // Build trigger node config, including filters if present
        const triggerNodeConfig = {
          ...(config.trigger.config || {}),
          ...(config.trigger.filters ? { filters: config.trigger.filters } : {}),
        };

        const triggerNode: WorkflowReactFlowNode = {
          id: 'node-trigger',
          type: 'trigger',
          position: triggerLayoutNode?.position || { x: 250, y: 80 },
          sourcePosition: Position.Bottom,
          targetPosition: Position.Top,
          data: {
            name: config.trigger.name || getTriggerDisplayName(config.trigger.type),
            icon: getTriggerIconName(config.trigger.type),
            status: 'valid',
            config: triggerNodeConfig,
            actionType: config.trigger.type,
            appName: appInfo.appName,
            appService: appInfo.appService,
          },
        };
        loadedNodes.push(triggerNode);
      }

      // Parse actions from graph map.
      const actionsMap = config.actions || {};
      const actionEntries = Object.entries(actionsMap);

      actionEntries.forEach(([actionId, action], index) => {
        const actionLayoutNode = layoutNodes.find((n) => n.id === actionId);
        const appInfo = getAppInfo(action.type, action.integration);

        const actionNode: WorkflowReactFlowNode = {
          id: actionId,
          type: 'action',
          position: actionLayoutNode?.position || { x: 250, y: 80 + (index + 1) * 160 },
          sourcePosition: Position.Bottom,
          targetPosition: Position.Top,
          data: {
            name: action.name || getActionDisplayName(action.type),
            icon: getActionIconName(action.type),
            status: 'valid',
            config: action.config || {},
            actionType: action.type,
            label: action.label,
            appName: appInfo.appName,
            appService: appInfo.appService,
            configSummary: getConfigSummary(action.config || {}, action.type),
          },
        };
        loadedNodes.push(actionNode);
      });

      // Build a sourceHandle lookup from route maps for backwards compatibility.
      // Older saved workflows may have layout edges without sourceHandle.
      const routeHandleLookup = new Map<string, string>();
      actionEntries.forEach(([actionId, action]) => {
        const next = action.next;
        if (next && typeof next === 'object') {
          Object.entries(next).forEach(([routeName, targetId]) => {
            routeHandleLookup.set(`${actionId}->${targetId}`, routeName);
          });
        }
      });

      // Create edges from layout or derive from next pointers.
      if (layoutEdges.length > 0) {
        layoutEdges.forEach((edge) => {
          const sourceHandle = edge.sourceHandle || routeHandleLookup.get(`${edge.source}->${edge.target}`);
          loadedEdges.push({
            id: edge.id,
            source: edge.source,
            target: edge.target,
            ...(sourceHandle ? { sourceHandle } : {}),
            type: 'smoothstep',
          });
        });
      } else {
        // Build edges from entry_action and next pointers.
        const entryActionId = config.entry_action || actionEntries[0]?.[0];
        if (entryActionId) {
          loadedEdges.push({
            id: `edge-trigger-${entryActionId}`,
            source: 'node-trigger',
            target: entryActionId,
            type: 'smoothstep',
          });
        }

        // Follow next pointers to build remaining edges.
        actionEntries.forEach(([actionId, action]) => {
          const next = action.next;
          if (typeof next === 'string') {
            loadedEdges.push({
              id: `edge-${actionId}-${next}`,
              source: actionId,
              target: next,
              type: 'smoothstep',
            });
          } else if (next && typeof next === 'object') {
            // Route map -- create labeled edges for branching actions.
            Object.entries(next).forEach(([routeName, targetId]) => {
              loadedEdges.push({
                id: `edge-${actionId}-${routeName}-${targetId}`,
                source: actionId,
                sourceHandle: routeName,
                target: targetId,
                type: 'smoothstep',
                label: routeName,
              });
            });
          }
        });
      }

      return {
        nodes: autoLayoutNodes(loadedNodes, loadedEdges),
        edges: loadedEdges,
      };
    },
    [],
  );

  /**
   * Load a workflow by ID
   */
  const loadWorkflow = useCallback(
    async (id: string): Promise<LoadedWorkflow | null> => {
      setLoading(true);
      setLoadError(null);

      try {
        const workflow = await workflowService.getWorkflow(parseInt(id));

        // Update workflow metadata in store
        const loadedName = workflow.name || 'Untitled Workflow';
        setId(parseInt(id));
        setWorkflowName(loadedName);
        setInitialName(loadedName);
        setIsActive(workflow.status === 'active');

        // Update version state
        setPublishedVersionId(workflow.published_version_id || null);
        setLastPublishedAt(workflow.last_published_at || null);
        setHasUnpublishedChanges(workflow.has_unpublished_changes ?? false);

        // Parse config
        const config = workflow.draft_config;
        const layoutNodes = config.layout?.nodes || [];
        const layoutEdges = config.layout?.edges || [];

        const result = parseWorkflowConfig(config, layoutNodes, layoutEdges);
        setLoading(false);
        return result;
      } catch (err) {
        setLoadError(getErrorMessage(err, 'Failed to load workflow'));
        setLoading(false);
        console.error('Error loading workflow:', err);
        return null;
      }
    },
    [
      parseWorkflowConfig,
      setWorkflowName,
      setInitialName,
      setIsActive,
      setId,
      setPublishedVersionId,
      setLastPublishedAt,
      setHasUnpublishedChanges,
    ],
  );

  /**
   * Retry loading a workflow
   */
  const retryLoad = useCallback(
    async (id: string): Promise<LoadedWorkflow | null> => {
      return loadWorkflow(id);
    },
    [loadWorkflow],
  );

  /**
   * Clear load error
   */
  const clearLoadError = useCallback(() => {
    setLoadError(null);
  }, []);

  /**
   * Save the current workflow
   */
  const saveWorkflow = useCallback(
    async (data: SaveWorkflowData): Promise<boolean> => {
      const { nodes, edges, workflowName: name } = data;

      setIsSaving(true);

      try {
        // Find trigger and action nodes
        const triggerNode = nodes.find((n) => n.type === 'trigger');
        const actionNodes = nodes.filter((n) => n.type === 'action');

        if (!triggerNode) {
          throw new Error('Workflow must have a trigger');
        }

        if (actionNodes.length === 0) {
          throw new Error('Workflow must have at least one action');
        }

        // Build workflow config with layout data
        const triggerConfig = triggerNode.data.config || {};
        const { filters, ...triggerConfigWithoutFilters } = triggerConfig as NodeConfig & {
          filters?: Record<string, unknown> | null;
        };

        // Build actions map from action nodes.
        const actionsMap: Record<string, ActionConfig> = {};

        // First pass: create action entries without next pointers.
        actionNodes.forEach((node) => {
          const actionConfig: ActionConfig = {
            id: node.id,
            type: node.data.actionType || 'wordpress/create_post',
            config: (node.data.config || {}) as NodeConfig,
            next: null,
          };

          if (node.data.label) actionConfig.label = node.data.label;
          if (node.data.name) actionConfig.name = node.data.name;
          if (node.data.appService) actionConfig.integration = node.data.appService;

          actionsMap[node.id] = actionConfig;
        });

        // Second pass: derive next pointers from edges.
        let entryActionId: string | undefined;

        // Find entry action (edge from trigger node).
        const triggerEdge = edges.find((e) => e.source === 'node-trigger');
        if (triggerEdge) {
          entryActionId = triggerEdge.target;
        }

        // Build next pointers from action-to-action edges.
        const edgesBySource = new Map<string, Edge[]>();
        edges.forEach((edge) => {
          if (edge.source === 'node-trigger') return; // Skip trigger edge.
          const existing = edgesBySource.get(edge.source) || [];
          existing.push(edge);
          edgesBySource.set(edge.source, existing);
        });

        edgesBySource.forEach((sourceEdges, sourceId) => {
          if (!actionsMap[sourceId]) return;

          if (sourceEdges.length === 1 && !sourceEdges[0].sourceHandle) {
            // Single non-branching edge.
            actionsMap[sourceId].next = sourceEdges[0].target;
          } else {
            // Multiple edges = route map (branching action).
            const routeMap: Record<string, string> = {};
            sourceEdges.forEach((edge) => {
              const routeName =
                edge.sourceHandle || (typeof edge.label === 'string' ? edge.label : 'default');
              routeMap[routeName] = edge.target;
            });
            actionsMap[sourceId].next = routeMap;
          }
        });

        const workflowConfig: WorkflowConfig = {
          trigger: {
            type: triggerNode.data.actionType || 'wordpress/user_registration',
            config: triggerConfigWithoutFilters,
            filters: filters || null,
            name: triggerNode.data.name,
            integration: triggerNode.data.appService,
          },
          actions: actionsMap,
          entry_action: entryActionId,
          layout: {
            nodes: nodes.map((n) => ({
              id: n.id,
              position: n.position,
            })),
            edges: edges.map((e) => ({
              id: e.id,
              source: e.source,
              target: e.target,
              ...(e.sourceHandle ? { sourceHandle: e.sourceHandle } : {}),
            })),
          },
        };

        const workflowData = {
          name: name || 'Untitled Workflow',
          description: '',
          trigger_type: triggerNode.data.actionType || 'wordpress/user_registration',
          config: workflowConfig,
        };

        if (workflowId) {
          // Update existing workflow
          await workflowService.updateWorkflow(parseInt(workflowId), workflowData);
        } else {
          // Create new workflow and notify parent of the new ID
          const newWorkflow = await workflowService.createWorkflow(workflowData);
          setId(newWorkflow.id);
          onWorkflowCreated?.(newWorkflow.id);
        }

        setIsSaving(false);
        return true;
      } catch (err) {
        toast.error(getErrorMessage(err, __('Failed to save workflow')));
        setIsSaving(false);
        return false;
      }
    },
    [workflowId, setIsSaving, setId, onWorkflowCreated],
  );

  return {
    loading,
    loadError,
    loadWorkflow,
    saveWorkflow,
    retryLoad,
    clearLoadError,
  };
}
