import React, { useCallback, useEffect, useState } from 'react';
import ReactFlow, { Controls, useNodesState, useEdgesState, addEdge, Node, Edge } from 'reactflow';
import { FiFile } from 'react-icons/fi';

import 'reactflow/dist/base.css';
import './index.css';
import TurboNode, { TurboNodeData } from './TurboNode';
import TurboEdge from './TurboEdge';
import FunctionIcon from './FunctionIcon';
import Sidebar from './Sidebar';

const initialNodes: Node<TurboNodeData>[] = [
  {
    id: '1',
    position: { x: 0, y: 0 },
    data: {
      icon: <FunctionIcon />,
      title: 'LLM Provider',
      subline: 'Select the LLM provider',
      inputFields: [],
      options: [
        { id: 'provider-openai', label: 'OpenAI', checked: true },
        { id: 'provider-azure', label: 'Azure', checked: false },
        { id: 'provider-cohere', label: 'Cohere', checked: false },
        { id: 'provider-huggingface', label: 'Hugging Face', checked: false },
        { id: 'provider-anthropic', label: 'Anthropic', checked: false },
      ],
    },
    type: 'turbo',
  },
  {
    id: '2',
    position: { x: 250, y: 0 },
    data: {
      icon: <FunctionIcon />,
      title: 'Model Selection',
      subline: 'Choose the LLM model',
      inputFields: [],
      options: [
        { id: 'model-gpt-3.5-turbo', label: 'GPT-3.5-turbo', checked: true },
        { id: 'model-gpt-4', label: 'GPT-4', checked: false },
        { id: 'model-claude', label: 'Claude', checked: false },
        { id: 'model-flan-t5', label: 'Flan-T5', checked: false },
      ],
    },
    type: 'turbo',
  },
  {
    id: '3',
    position: { x: 500, y: 0 },
    data: {
      icon: <FunctionIcon />,
      title: 'Prompt Engineering',
      subline: 'Configure prompt settings',
      inputFields: [
        { id: 'prompt-template', label: 'Prompt Template', value: '' },
        { id: 'prompt-examples', label: 'Prompt Examples', value: '' },
      ],
      options: [
        { id: 'prompt-fewshot', label: 'Few-shot Learning', checked: false },
        { id: 'prompt-cot', label: 'Chain-of-Thought', checked: false },
      ],
    },
    type: 'turbo',
  },
  {
    id: '4',
    position: { x: 750, y: 0 },
    data: {
      icon: <FunctionIcon />,
      title: 'API Settings',
      subline: 'Set API parameters',
      inputFields: [
        { id: 'api-key', label: 'API Key', value: '' },
        { id: 'api-base', label: 'API Base URL', value: '' },
      ],
      options: [
        { id: 'api-streaming', label: 'Enable Streaming', checked: false },
        { id: 'api-retry', label: 'Enable Auto-Retry', checked: true },
      ],
    },
    type: 'turbo',
  },
  {
    id: '5',
    position: { x: 1000, y: 0 },
    data: {
      icon: <FiFile />,
      title: 'Output',
      subline: 'Generated output from LLM',
    },
    type: 'turbo',
  },
];

const initialEdges: Edge[] = [
  {
    id: 'e1-2',
    source: '1',
    target: '2',
  },
  {
    id: 'e2-3',
    source: '2',
    target: '3',
  },
  {
    id: 'e3-4',
    source: '3',
    target: '4',
  },
  {
    id: 'e4-5',
    source: '4',
    target: '5',
  },
];

const nodeTypes = {
  turbo: TurboNode,
};

const edgeTypes = {
  turbo: TurboEdge,
};

const defaultEdgeOptions = {
  type: 'turbo',
  markerEnd: 'edge-circle',
};

const App = () => {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const [recursiveSteps, setRecursiveSteps] = useState(1);
  const [inputData, setInputData] = useState('');

  const onConnect = useCallback((params) => setEdges((els) => addEdge(params, els)), []);

  const addRecursiveStep = () => {
    setRecursiveSteps((prevSteps) => prevSteps + 1);
  };

  const removeRecursiveStep = () => {
    if (recursiveSteps > 1) {
      setRecursiveSteps((prevSteps) => prevSteps - 1);
    }
  };

  const handleInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
    setInputData(event.target.value);
  };

  useEffect(() => {
    // Update nodes based on recursive steps and input data
    // You can modify the nodes array or create new nodes dynamically
    // based on the recursiveSteps and inputData values
    // For example:
    // const newNodes = [...nodes];
    // for (let i = 0; i < recursiveSteps; i++) {
    //   newNodes.push({
    //     id: `recursive-${i}`,
    //     position: { x: 100 * i, y: 200 },
    //     data: {
    //       icon: <FunctionIcon />,
    //       title: `Recursive Step ${i + 1}`,
    //       subline: 'Recursive prompt',
    //       inputFields: [
    //         { id: `recursive-input-${i}`, label: `Input ${i + 1}`, value: '' },
    //       ],
    //       options: [],
    //     },
    //     type: 'turbo',
    //   });
    // }
    // setNodes(newNodes);
  }, [recursiveSteps, inputData]);

  return (
    <div className="flow-container">
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        fitView
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        defaultEdgeOptions={defaultEdgeOptions}
      >
        <Controls showInteractive={false} />
        <svg>
          <defs>
            <linearGradient id="edge-gradient">
              <stop offset="0%" stopColor="#ae53ba" />
              <stop offset="100%" stopColor="#2a8af6" />
            </linearGradient>

            <marker
              id="edge-circle"
              viewBox="-5 -5 10 10"
              refX="0"
              refY="0"
              markerUnits="strokeWidth"
              markerWidth="10"
              markerHeight="10"
              orient="auto"
            >
              <circle stroke="#2a8af6" strokeOpacity="0.75" r="2" cx="0" cy="0" />
            </marker>
          </defs>
        </svg>
      </ReactFlow>
      <Sidebar
        recursiveSteps={recursiveSteps}
        addRecursiveStep={addRecursiveStep}
        removeRecursiveStep={removeRecursiveStep}
        inputData={inputData}
        handleInputChange={handleInputChange}
      />
    </div>
  );
};

export default App;