/**
 * Automation2FlowEditor
 *
 * n8n-style flow builder with backend-driven node list.
 * Composes: NodeSidebar, FlowCanvas, NodeConfigPanel, CanvasHeader.
 */

import React, { useState, useEffect, useCallback } from 'react';
import { FaSpinner } from 'react-icons/fa';
import { useApiRequest } from '../../hooks/useApi';
import {
  fetchNodeTypes,
  executeGraph,
  fetchWorkflows,
  fetchWorkflow,
  createWorkflow,
  updateWorkflow,
  type NodeType,
  type NodeTypeCategory,
  type Automation2Graph,
  type Automation2Workflow,
  type ExecuteGraphResponse,
} from '../../api/automation2Api';
import { FlowCanvas, type CanvasNode, type CanvasConnection } from './FlowCanvas';
import { NodeConfigPanel } from './NodeConfigPanel';
import { NodeSidebar } from './NodeSidebar';
import { CanvasHeader } from './CanvasHeader';
import { getCategoryIcon } from './utils';
import { fromApiGraph, toApiGraph } from './graphUtils';
import styles from './Automation2FlowEditor.module.css';

const LOG = '[Automation2]';

interface Automation2FlowEditorProps {
  instanceId: string;
  language?: string;
  /** When set, load this workflow on mount (e.g. from workflows list edit) */
  initialWorkflowId?: string | null;
}

export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({
  instanceId,
  language = 'de',
  initialWorkflowId,
}) => {
  const { request } = useApiRequest();
  const [nodeTypes, setNodeTypes] = useState<NodeType[]>([]);
  const [categories, setCategories] = useState<NodeTypeCategory[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [filter, setFilter] = useState('');
  const [expandedCategories, setExpandedCategories] = useState<Set<string>>(
    new Set(['trigger', 'input', 'flow', 'data', 'ai', 'email', 'sharepoint'])
  );
  const [canvasNodes, setCanvasNodes] = useState<CanvasNode[]>([]);
  const [canvasConnections, setCanvasConnections] = useState<CanvasConnection[]>([]);
  const [executing, setExecuting] = useState(false);
  const [executeResult, setExecuteResult] = useState<ExecuteGraphResponse | null>(null);
  const [workflows, setWorkflows] = useState<Automation2Workflow[]>([]);
  const [currentWorkflowId, setCurrentWorkflowId] = useState<string | null>(null);
  const [selectedNode, setSelectedNode] = useState<CanvasNode | null>(null);
  const [saving, setSaving] = useState(false);

  const handleFromApiGraph = useCallback(
    (graph: Automation2Graph) => {
      const { nodes, connections } = fromApiGraph(graph, nodeTypes);
      setCanvasNodes(nodes);
      setCanvasConnections(connections);
    },
    [nodeTypes]
  );

  const handleExecute = useCallback(async () => {
    const graph = toApiGraph(canvasNodes, canvasConnections);
    if (graph.nodes.length === 0) {
      setExecuteResult({ success: false, error: 'Keine Nodes im Workflow.' });
      return;
    }
    setExecuting(true);
    setExecuteResult(null);
    try {
      const result = await executeGraph(
        request,
        instanceId,
        graph,
        currentWorkflowId ?? undefined
      );
      setExecuteResult(result);
    } catch (err: unknown) {
      setExecuteResult({ success: false, error: err instanceof Error ? err.message : String(err) });
    } finally {
      setExecuting(false);
    }
  }, [request, instanceId, canvasNodes, canvasConnections, currentWorkflowId]);

  const loadWorkflows = useCallback(async () => {
    if (!instanceId) return;
    try {
      const items = await fetchWorkflows(request, instanceId);
      setWorkflows(items);
    } catch (e) {
      console.error(`${LOG} loadWorkflows failed`, e);
    }
  }, [instanceId, request]);

  const handleSave = useCallback(async () => {
    const graph = toApiGraph(canvasNodes, canvasConnections);
    if (graph.nodes.length === 0) {
      setExecuteResult({ success: false, error: 'Keine Nodes zum Speichern.' });
      return;
    }
    setSaving(true);
    try {
      if (currentWorkflowId) {
        await updateWorkflow(request, instanceId, currentWorkflowId, { graph });
        setExecuteResult({ success: true } as ExecuteGraphResponse);
      } else {
        const label = prompt('Workflow-Name:', 'Neuer Workflow') || 'Neuer Workflow';
        const created = await createWorkflow(request, instanceId, { label, graph });
        setCurrentWorkflowId(created.id);
        setWorkflows((prev) => [...prev, created]);
        setExecuteResult({ success: true } as ExecuteGraphResponse);
      }
    } catch (err: unknown) {
      setExecuteResult({ success: false, error: err instanceof Error ? err.message : String(err) });
    } finally {
      setSaving(false);
    }
  }, [request, instanceId, canvasNodes, canvasConnections, currentWorkflowId]);

  const handleLoad = useCallback(
    async (workflowId: string) => {
      try {
        const wf = await fetchWorkflow(request, instanceId, workflowId);
        if (wf.graph) handleFromApiGraph(wf.graph);
      } catch (err: unknown) {
        setExecuteResult({
          success: false,
          error: err instanceof Error ? err.message : String(err),
        });
      }
    },
    [request, instanceId, handleFromApiGraph]
  );

  const handleWorkflowSelect = useCallback(
    (workflowId: string | null) => {
      setCurrentWorkflowId(workflowId);
      if (workflowId) handleLoad(workflowId);
      else {
        setCanvasNodes([]);
        setCanvasConnections([]);
        setExecuteResult(null);
      }
    },
    [handleLoad]
  );

  const handleNew = useCallback(() => {
    setCanvasNodes([]);
    setCanvasConnections([]);
    setCurrentWorkflowId(null);
    setExecuteResult(null);
  }, []);

  const handleNodeParametersChange = useCallback((nodeId: string, parameters: Record<string, unknown>) => {
    setCanvasNodes((prev) => prev.map((n) => (n.id === nodeId ? { ...n, parameters } : n)));
  }, []);

  const loadNodeTypes = useCallback(async () => {
    if (!instanceId) return;
    setLoading(true);
    setError(null);
    try {
      const data = await fetchNodeTypes(request, instanceId, language);
      setNodeTypes(data.nodeTypes);
      setCategories(data.categories);
    } catch (err: unknown) {
      setError(err instanceof Error ? err.message : String(err));
      setNodeTypes([]);
      setCategories([]);
    } finally {
      setLoading(false);
    }
  }, [instanceId, language, request]);

  useEffect(() => {
    loadNodeTypes();
  }, [loadNodeTypes]);

  useEffect(() => {
    loadWorkflows();
  }, [loadWorkflows]);

  useEffect(() => {
    if (initialWorkflowId && workflows.length > 0 && !currentWorkflowId) {
      handleWorkflowSelect(initialWorkflowId);
    }
  }, [initialWorkflowId, workflows, currentWorkflowId, handleWorkflowSelect]);

  const toggleCategory = useCallback((id: string) => {
    setExpandedCategories((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }, []);

  const handleDropNodeType = useCallback(
    (nodeTypeId: string, x: number, y: number) => {
      const nt = nodeTypes.find((n) => n.id === nodeTypeId);
      if (!nt) return;
      const id = `n_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
      const label =
        typeof nt.label === 'string' ? nt.label : (nt.label as Record<string, string>)?.[language] ?? nt.id;
      setCanvasNodes((prev) => [
        ...prev,
        {
          id,
          type: nodeTypeId,
          x,
          y,
          label,
          title: label,
          color: nt.meta?.color,
          inputs: nt.inputs ?? 1,
          outputs: nt.outputs ?? 1,
          parameters: {},
        },
      ]);
    },
    [nodeTypes, language]
  );

  const renderSidebar = () => {
    if (loading) {
      return (
        <div className={styles.sidebar}>
          <div className={styles.sidebarHeader}>
            <h3 className={styles.sidebarTitle}>Nodes</h3>
          </div>
          <div className={styles.loading}>
            <FaSpinner className={styles.spinner} style={{ marginBottom: '0.5rem' }} />
            <p>Lade Node-Typen...</p>
          </div>
        </div>
      );
    }
    if (error) {
      return (
        <div className={styles.sidebar}>
          <div className={styles.sidebarHeader}>
            <h3 className={styles.sidebarTitle}>Nodes</h3>
          </div>
          <div className={styles.error}>
            <p>{error}</p>
            <button className={styles.retryButton} onClick={loadNodeTypes}>
              Erneut versuchen
            </button>
          </div>
        </div>
      );
    }
    return (
      <NodeSidebar
        nodeTypes={nodeTypes}
        categories={categories}
        filter={filter}
        onFilterChange={setFilter}
        language={language}
        expandedCategories={expandedCategories}
        onToggleCategory={toggleCategory}
      />
    );
  };

  return (
    <div className={styles.container}>
      {renderSidebar()}

      <div className={styles.canvas}>
        <CanvasHeader
          workflows={workflows}
          currentWorkflowId={currentWorkflowId}
          onWorkflowSelect={handleWorkflowSelect}
          onNew={handleNew}
          onSave={handleSave}
          onExecute={handleExecute}
          saving={saving}
          executing={executing}
          hasNodes={canvasNodes.length > 0}
          executeResult={executeResult}
        />
        <div className={styles.canvasArea} style={{ display: 'flex', flex: 1, minWidth: 0 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <FlowCanvas
              nodes={canvasNodes}
              connections={canvasConnections}
              nodeTypes={nodeTypes}
              onNodesChange={setCanvasNodes}
              onConnectionsChange={setCanvasConnections}
              onDropNodeType={handleDropNodeType}
              getLabel={(node) => node.title ?? node.label ?? node.type}
              getCategoryIcon={getCategoryIcon}
              onSelectionChange={setSelectedNode}
            />
          </div>
          {selectedNode &&
            ['input.', 'ai.', 'email.', 'sharepoint.'].some((p) =>
              selectedNode.type.startsWith(p)
            ) && (
            <NodeConfigPanel
              node={selectedNode}
              nodeType={nodeTypes.find((nt) => nt.id === selectedNode.type)}
              language={language}
              onParametersChange={handleNodeParametersChange}
              instanceId={instanceId}
              request={request}
            />
          )}
        </div>
      </div>
    </div>
  );
};

export default Automation2FlowEditor;
