import { useState, useCallback, useRef, useEffect, type ChangeEvent, type DragEvent } from 'react'
import {
  ReactFlow,
  Background,
  Controls,
  MiniMap,
  addEdge,
  useNodesState,
  useEdgesState,
  Handle,
  Position,
  type Node,
  type Edge,
  type Connection,
  type NodeTypes,
} from '@xyflow/react'
import '@xyflow/react/dist/style.css'
import { useThemeStore, resolveTheme } from '../store/theme'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import {
  getWorkflows,
  createWorkflow,
  updateWorkflow,
  deleteWorkflow,
  getNodeDefinitions,
  createPresetWorkflowConfig,
  getWorkflowPresetType,
  type WorkflowDefinition,
  type WorkflowConfig,
  type WorkflowEdge,
  type WorkflowPresetType,
  type WorkflowParams,
  type StepCategory,
  type WorkflowNodeDefinition,
  type WorkflowNodeFieldDefinition,
} from '../api/workflows'
import {
  FileUp,
  RefreshCw,
  Camera,
  Film,
  Layers,
  Download,
  Bell,
  Plus,
  Save,
  Trash2,
  GitBranch,
  X,
} from 'lucide-react'
import { toast } from 'sonner'

function normalizeWorkflowParams(params: WorkflowParams): WorkflowParams {
  const normalized = { ...params }
  const resolution = Array.isArray(normalized.resolution) ? normalized.resolution : undefined
  if (resolution && resolution.length === 2) {
    normalized.width = Number(resolution[0])
    normalized.height = Number(resolution[1])
    delete normalized.resolution
  }
  return normalized
}

type WorkflowCanvasNodeData = {
  label: string
  params: WorkflowParams
  step: string
  description?: string
  icon?: string
  category?: StepCategory
}

function renderWorkflowIcon(iconName?: string, size = 14) {
  switch (iconName) {
    case 'file-up':
      return <FileUp size={size} />
    case 'film':
      return <Film size={size} />
    case 'layers':
      return <Layers size={size} />
    case 'download':
      return <Download size={size} />
    case 'bell':
      return <Bell size={size} />
    case 'camera':
      return <Camera size={size} />
    case 'refresh-cw':
    default:
      return <RefreshCw size={size} />
  }
}

function buildNodeData(
  step: string,
  params: WorkflowParams = {},
  definition?: WorkflowNodeDefinition,
  overrides?: Partial<WorkflowCanvasNodeData>,
): WorkflowCanvasNodeData {
  return {
    label: overrides?.label ?? definition?.label ?? inferNodeLabel(step),
    params: normalizeWorkflowParams(params),
    step,
    description: overrides?.description ?? definition?.description,
    icon: overrides?.icon ?? definition?.icon,
    category: overrides?.category ?? definition?.category,
  }
}

// ─── Custom Node Components ──────────────────────────────────────────────────

interface BaseNodeProps {
  label: string
  icon: React.ReactNode
  accentClass: string
  description?: string
  selected?: boolean
  hasSource?: boolean
  hasTarget?: boolean
}

function BaseNode({ label, icon, accentClass, description, selected, hasSource = true, hasTarget = true }: BaseNodeProps) {
  return (
    <div
      className={`rounded-lg border-2 p-3 min-w-[140px] bg-surface shadow-sm transition-colors ${
        selected ? 'border-accent' : 'border-border-default'
      }`}
    >
      {hasTarget && (
        <Handle type="target" position={Position.Left} className="w-3 h-3 bg-content-muted border-2 border-surface" />
      )}
      <div className={`flex items-center gap-2 mb-1 ${accentClass}`}>
        {icon}
        <span className="font-medium text-sm">{label}</span>
      </div>
      {description && <p className="text-xs text-content-muted">{description}</p>}
      {hasSource && (
        <Handle type="source" position={Position.Right} className="w-3 h-3 bg-content-muted border-2 border-surface" />
      )}
    </div>
  )
}

function InputNode({ data, selected }: { data: WorkflowCanvasNodeData; selected?: boolean }) {
  return (
    <BaseNode
      label={data.label}
      icon={renderWorkflowIcon(data.icon)}
      accentClass="text-green-600"
      description={data.description}
      selected={selected}
      hasTarget={false}
    />
  )
}

function ConvertNode({ data, selected }: { data: WorkflowCanvasNodeData; selected?: boolean }) {
  return (
    <BaseNode
      label={data.label}
      icon={renderWorkflowIcon(data.icon)}
      accentClass="text-blue-600"
      description={data.description}
      selected={selected}
    />
  )
}

function ProcessNode({ data, selected }: { data: WorkflowCanvasNodeData; selected?: boolean }) {
  return (
    <BaseNode
      label={data.label}
      icon={renderWorkflowIcon(data.icon)}
      accentClass="text-sky-600"
      description={data.description}
      selected={selected}
    />
  )
}

function RenderNode({ data, selected }: { data: WorkflowCanvasNodeData; selected?: boolean }) {
  const params = data.params ?? {}
  return (
    <BaseNode
      label={data.label}
      icon={renderWorkflowIcon(data.icon)}
      accentClass="text-orange-600"
      description={
        params.render_engine
          ? `${params.render_engine} · ${params.samples ?? 256} samples`
          : data.description
      }
      selected={selected}
    />
  )
}

function RenderFramesNode({ data, selected }: { data: WorkflowCanvasNodeData; selected?: boolean }) {
  const params = data.params ?? {}
  return (
    <BaseNode
      label={data.label}
      icon={renderWorkflowIcon(data.icon)}
      accentClass="text-orange-600"
      description={params.fps ? `${params.fps} fps · ${params.duration_s ?? '?'}s` : data.description}
      selected={selected}
    />
  )
}

function OutputNode({ data, selected }: { data: WorkflowCanvasNodeData; selected?: boolean }) {
  return (
    <BaseNode
      label={data.label}
      icon={renderWorkflowIcon(data.icon)}
      accentClass="text-slate-600"
      description={data.description}
      selected={selected}
      hasSource={false}
    />
  )
}

const nodeTypes: NodeTypes = {
  inputNode: InputNode as any,
  convertNode: ConvertNode as any,
  processNode: ProcessNode as any,
  renderNode: RenderNode as any,
  renderFramesNode: RenderFramesNode as any,
  outputNode: OutputNode as any,
}

function inferNodeType(step: string): string {
  if (step === 'resolve_step_path') return 'inputNode'
  if (step === 'stl_cache_generate') return 'convertNode'
  if (step === 'blender_turntable') return 'renderFramesNode'
  if (step === 'output_save' || step === 'export_blend' || step === 'notify' || step === 'thumbnail_save') return 'outputNode'
  if (step.startsWith('blender_') || step === 'threejs_render') return 'renderNode'
  if (step.startsWith('occ_') || step === 'glb_bbox' || step === 'material_map_resolve' || step === 'auto_populate_materials') {
    return 'processNode'
  }
  return 'renderNode'
}

function inferNodeLabel(step: string): string {
  return step
    .split('_')
    .map(part => part.charAt(0).toUpperCase() + part.slice(1))
    .join(' ')
}

function inferStepFromNodeType(type?: string): string {
  if (type === 'inputNode') return 'resolve_step_path'
  if (type === 'convertNode') return 'stl_cache_generate'
  if (type === 'processNode') return 'order_line_setup'
  if (type === 'renderFramesNode') return 'blender_turntable'
  if (type === 'outputNode') return 'output_save'
  return 'blender_still'
}

function workflowToGraph(
  config: WorkflowConfig,
  nodeDefinitionsByStep: Record<string, WorkflowNodeDefinition>,
): { nodes: Node[]; edges: Edge[] } {
  return {
    nodes: config.nodes.map(node => ({
      id: node.id,
      type: node.ui?.type ?? nodeDefinitionsByStep[node.step]?.node_type ?? inferNodeType(node.step),
      position: node.ui?.position ?? { x: 0, y: 0 },
      data: buildNodeData(node.step, node.params ?? {}, nodeDefinitionsByStep[node.step], {
        label: node.ui?.label ?? undefined,
      }),
    })),
    edges: config.edges.map((edge, index) => ({
      id: `e_${edge.from}_${edge.to}_${index}`,
      source: edge.from,
      target: edge.to,
    })),
  }
}

// ─── Config Sidepanel ─────────────────────────────────────────────────────────

function groupFieldsBySection(fields: WorkflowNodeFieldDefinition[]) {
  return fields.reduce<Record<string, WorkflowNodeFieldDefinition[]>>((sections, field) => {
    const section = field.section || 'General'
    sections[section] = [...(sections[section] ?? []), field]
    return sections
  }, {})
}

function ConfigSidepanel({
  params,
  onChange,
  nodeDefinition,
  step,
  onStepChange,
  nodeDefinitions,
}: {
  params: WorkflowParams
  onChange: (p: WorkflowParams) => void
  nodeDefinition?: WorkflowNodeDefinition
  step?: string
  onStepChange?: (step: string) => void
  nodeDefinitions: WorkflowNodeDefinition[]
}) {
  const updateField = (field: WorkflowNodeFieldDefinition, value: unknown) => {
    onChange(
      normalizeWorkflowParams({
        ...params,
        [field.key]: value,
      }),
    )
  }

  const handleNumberChange = (field: WorkflowNodeFieldDefinition, event: ChangeEvent<HTMLInputElement>) => {
    const rawValue = event.target.value
    if (rawValue === '') {
      const nextParams = { ...params }
      delete nextParams[field.key]
      onChange(nextParams)
      return
    }
    updateField(field, Number(rawValue))
  }

  const fieldsBySection = groupFieldsBySection(nodeDefinition?.fields ?? [])

  return (
    <div className="w-72 border-l border-border-default bg-surface p-4 space-y-5 overflow-y-auto">
      <h3 className="font-semibold text-content">Node Configuration</h3>

      {nodeDefinitions.length > 0 && onStepChange && (
        <div>
          <label className="text-sm text-content-secondary mb-2 block">Workflow Node</label>
          <select
            value={step ?? ''}
            onChange={event => onStepChange(event.target.value)}
            className="w-full border border-border-default rounded-lg px-3 py-2 text-sm bg-surface text-content focus:outline-none focus:ring-2 focus:ring-accent"
          >
            {nodeDefinitions.map(definition => (
              <option key={definition.step} value={definition.step}>
                {definition.label}
              </option>
            ))}
          </select>
          {nodeDefinition && (
            <div className="mt-2 space-y-1">
              <p className="text-xs text-content-muted">{nodeDefinition.description}</p>
              <span
                className={`inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${
                  nodeDefinition.execution_kind === 'bridge'
                    ? 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300'
                    : 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300'
                }`}
              >
                {nodeDefinition.execution_kind === 'bridge' ? 'Legacy Bridge' : 'Native Node'}
              </span>
            </div>
          )}
        </div>
      )}

      {Object.keys(fieldsBySection).length === 0 && (
        <p className="text-sm text-content-muted">
          This node currently has no configurable settings in the editor.
        </p>
      )}

      {Object.entries(fieldsBySection).map(([section, fields]) => (
        <div key={section} className="space-y-3">
          <h4 className="text-xs font-semibold uppercase tracking-wide text-content-secondary">
            {section}
          </h4>
          {fields.map(field => {
            const rawValue = params[field.key]
            const value = rawValue ?? field.default

            return (
              <div key={field.key}>
                <label className="text-sm text-content-secondary mb-1 block">
                  {field.label}
                  {field.unit ? ` (${field.unit})` : ''}
                </label>
                {field.type === 'select' && (
                  <select
                    value={String(value ?? '')}
                    onChange={event => updateField(field, event.target.value)}
                    className="w-full border border-border-default rounded-lg px-3 py-2 text-sm bg-surface text-content focus:outline-none focus:ring-2 focus:ring-accent"
                  >
                    {field.options.map(option => (
                      <option key={String(option.value)} value={String(option.value)}>
                        {option.label}
                      </option>
                    ))}
                  </select>
                )}
                {field.type === 'number' && (
                  <input
                    type="number"
                    min={field.min ?? undefined}
                    max={field.max ?? undefined}
                    step={field.step ?? undefined}
                    value={typeof value === 'number' ? value : value == null ? '' : Number(value)}
                    onChange={event => handleNumberChange(field, event)}
                    className="w-full border border-border-default rounded-lg px-3 py-2 text-sm bg-surface text-content focus:outline-none focus:ring-2 focus:ring-accent"
                  />
                )}
                {field.type === 'boolean' && (
                  <label className="flex items-center gap-2 rounded-lg border border-border-default px-3 py-2 text-sm text-content">
                    <input
                      type="checkbox"
                      checked={Boolean(value)}
                      onChange={event => updateField(field, event.target.checked)}
                      className="accent-accent"
                    />
                    <span>{Boolean(value) ? 'Enabled' : 'Disabled'}</span>
                  </label>
                )}
                {field.description && (
                  <p className="mt-1 text-xs text-content-muted">{field.description}</p>
                )}
              </div>
            )
          })}
        </div>
      ))}
    </div>
  )
}

// ─── Node Definitions Panel ───────────────────────────────────────────────────

const CATEGORY_LABELS: Record<StepCategory, string> = {
  input: 'Input',
  processing: 'Processing',
  rendering: 'Rendering',
  output: 'Output',
}

const CATEGORY_COLORS: Record<StepCategory, string> = {
  input: 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300',
  processing: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300',
  rendering: 'bg-orange-100 text-orange-700 dark:bg-orange-900/40 dark:text-orange-300',
  output: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300',
}

function NodeDefinitionsPanel({ definitions }: { definitions: WorkflowNodeDefinition[] }) {
  const [expanded, setExpanded] = useState<StepCategory | null>(null)

  const grouped = definitions.reduce<Record<StepCategory, WorkflowNodeDefinition[]>>(
    (acc, definition) => {
      acc[definition.category] = [...(acc[definition.category] ?? []), definition]
      return acc
    },
    { input: [], processing: [], rendering: [], output: [] },
  )

  const categories: StepCategory[] = ['input', 'processing', 'rendering', 'output']

  return (
    <div className="border-t border-border-default pt-3 mt-3">
      <p className="text-xs font-semibold text-content-secondary uppercase tracking-wide mb-2">
        Available Nodes
      </p>
      <div className="space-y-1">
        {categories.map(cat => (
          <div key={cat}>
            <button
              onClick={() => setExpanded(expanded === cat ? null : cat)}
              className="w-full flex items-center justify-between text-xs text-content-secondary hover:text-content py-1"
            >
              <span className={`px-1.5 py-0.5 rounded-full font-medium ${CATEGORY_COLORS[cat]}`}>
                {CATEGORY_LABELS[cat]}
              </span>
              <span className="text-content-muted">{grouped[cat].length}</span>
            </button>
            {expanded === cat && (
              <div className="ml-2 mt-1 space-y-1">
                {grouped[cat].map(definition => (
                  <div
                    key={definition.step}
                    className="text-xs bg-surface-hover rounded px-2 py-1.5"
                    title={definition.description}
                  >
                    <div className="flex items-center justify-between gap-2">
                      <p className="font-medium text-content-secondary truncate">{definition.label}</p>
                      <span
                        className={`rounded-full px-1.5 py-0.5 text-[10px] font-medium ${
                          definition.execution_kind === 'bridge'
                            ? 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300'
                            : 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300'
                        }`}
                      >
                        {definition.execution_kind === 'bridge' ? 'Bridge' : 'Native'}
                      </span>
                    </div>
                    <p className="font-mono text-content-muted truncate mt-0.5">{definition.step}</p>
                    <p className="text-content-muted mt-0.5 line-clamp-2">{definition.description}</p>
                  </div>
                ))}
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  )
}

// ─── New Workflow Modal ───────────────────────────────────────────────────────

interface NewWorkflowModalProps {
  onClose: () => void
  onCreate: (name: string, type: WorkflowPresetType) => void
  isLoading: boolean
}

function NewWorkflowModal({ onClose, onCreate, isLoading }: NewWorkflowModalProps) {
  const [name, setName] = useState('')
  const [type, setType] = useState<WorkflowPresetType>('still')

  return (
    <div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
      <div className="bg-surface rounded-xl shadow-xl w-full max-w-md p-6">
        <div className="flex items-center justify-between mb-4">
          <h2 className="text-lg font-semibold text-content">New Workflow</h2>
          <button onClick={onClose} className="text-content-muted hover:text-content">
            <X size={20} />
          </button>
        </div>

        <div className="space-y-4">
          <div>
            <label className="block text-sm text-content-secondary mb-1">Name</label>
            <input
              className="w-full border border-border-default rounded-lg px-3 py-2 text-sm bg-surface text-content focus:outline-none focus:ring-2 focus:ring-accent"
              placeholder="e.g. Still Render Standard"
              value={name}
              onChange={e => setName(e.target.value)}
              autoFocus
            />
          </div>

          <div>
            <label className="block text-sm text-content-secondary mb-1">Type</label>
            <div className="grid grid-cols-2 gap-2">
              {([
                { value: 'still', label: 'Still', desc: 'Single PNG image' },
                { value: 'turntable', label: 'Turntable', desc: 'Animation MP4' },
                { value: 'multi_angle', label: 'Multi-Angle', desc: 'Multiple angles' },
                { value: 'still_with_exports', label: 'Still + GLB', desc: 'PNG + GLB exports' },
                { value: 'custom', label: 'Custom', desc: 'Free canvas' },
              ] as { value: WorkflowPresetType; label: string; desc: string }[]).map(opt => (
                <button
                  key={opt.value}
                  onClick={() => setType(opt.value)}
                  className={`p-3 rounded-lg border-2 text-left transition-colors ${
                    type === opt.value
                      ? 'border-accent bg-accent-light'
                      : 'border-border-default hover:border-border-light'
                  }`}
                >
                  <p className="text-sm font-medium text-content">{opt.label}</p>
                  <p className="text-xs text-content-muted mt-0.5">{opt.desc}</p>
                </button>
              ))}
            </div>
          </div>
        </div>

        <div className="flex justify-end gap-2 mt-6">
          <button
            onClick={onClose}
            className="px-4 py-2 text-sm rounded-lg border border-border-default text-content-secondary hover:bg-surface-hover"
          >
            Cancel
          </button>
          <button
            disabled={!name.trim() || isLoading}
            onClick={() => onCreate(name.trim(), type)}
            className="px-4 py-2 text-sm rounded-lg bg-accent text-white hover:bg-accent-hover disabled:opacity-50 disabled:cursor-not-allowed"
          >
            {isLoading ? 'Creating…' : 'Create'}
          </button>
        </div>
      </div>
    </div>
  )
}

// ─── Flow Canvas ──────────────────────────────────────────────────────────────

interface FlowCanvasProps {
  workflow: WorkflowDefinition
  onSave: (config: WorkflowConfig) => void
  isSaving: boolean
}

function FlowCanvas({ workflow, onSave, isSaving }: FlowCanvasProps) {
  const { data: nodeDefinitionsData } = useQuery({
    queryKey: ['workflow-node-definitions'],
    queryFn: getNodeDefinitions,
    staleTime: 5 * 60 * 1000,
  })
  const nodeDefinitions = nodeDefinitionsData?.definitions ?? []
  const nodeDefinitionsByStep = Object.fromEntries(nodeDefinitions.map(definition => [definition.step, definition]))
  const { nodes: initNodes, edges: initEdges } = workflowToGraph(workflow.config, nodeDefinitionsByStep)
  const [nodes, setNodes, onNodesChange] = useNodesState(initNodes)
  const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges)
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null)
  const reactFlowWrapper = useRef<HTMLDivElement>(null)
  const [reactFlowInstance, setReactFlowInstance] = useState<any>(null)

  useEffect(() => {
    const graph = workflowToGraph(workflow.config, nodeDefinitionsByStep)
    setNodes(graph.nodes)
    setEdges(graph.edges)
    setSelectedNodeId(null)
  }, [nodeDefinitionsData, setEdges, setNodes, workflow.config])

  const onConnect = useCallback(
    (connection: Connection) => setEdges(eds => addEdge(connection, eds)),
    [setEdges],
  )

  const onNodeClick = useCallback((_: React.MouseEvent, node: Node) => {
    setSelectedNodeId(node.id)
  }, [])

  const onPaneClick = useCallback(() => {
    setSelectedNodeId(null)
  }, [])

  const handleParamsChange = useCallback(
    (newParams: WorkflowParams) => {
      setNodes(nds =>
        nds.map(n => {
          if (n.id === selectedNodeId) {
            return { ...n, data: { ...n.data, params: normalizeWorkflowParams(newParams) } }
          }
          return n
        }),
      )
    },
    [selectedNodeId, setNodes],
  )

  const handlePipelineStepChange = useCallback(
    (stepName: string) => {
      const definition = nodeDefinitionsByStep[stepName]
      setNodes(nds =>
        nds.map(n => {
          if (n.id === selectedNodeId) {
            const currentData = (n.data as WorkflowCanvasNodeData | undefined) ?? buildNodeData(stepName)
            return {
              ...n,
              type: definition?.node_type ?? inferNodeType(stepName),
              data: {
                ...buildNodeData(
                  stepName || inferStepFromNodeType(n.type),
                  {
                    ...(definition?.defaults ?? {}),
                    ...currentData.params,
                  },
                  definition,
                ),
                step: stepName || inferStepFromNodeType(n.type),
              },
            }
          }
          return n
        }),
      )
    },
    [nodeDefinitionsByStep, selectedNodeId, setNodes],
  )

  // Drag-drop new nodes from palette
  const onDragOver = useCallback((event: DragEvent<HTMLDivElement>) => {
    event.preventDefault()
    event.dataTransfer.dropEffect = 'move'
  }, [])

  const onDrop = useCallback(
    (event: DragEvent<HTMLDivElement>) => {
      event.preventDefault()
      const step = event.dataTransfer.getData('application/workflow-step')
      if (!step || !reactFlowInstance) return

      const definition = nodeDefinitionsByStep[step]
      const type = definition?.node_type ?? inferNodeType(step)

      const position = reactFlowInstance.screenToFlowPosition({
        x: event.clientX,
        y: event.clientY,
      })

      const newNode: Node = {
        id: `${step}_${Date.now()}`,
        type,
        position,
        data: buildNodeData(step, definition?.defaults ?? {}, definition),
      }
      setNodes(nds => [...nds, newNode])
    },
    [nodeDefinitionsByStep, reactFlowInstance, setNodes],
  )

  const handleSave = () => {
    const updatedConfig: WorkflowConfig = {
      version: workflow.config.version ?? 1,
      ui: workflow.config.ui,
      nodes: nodes.map(node => ({
        id: node.id,
        step: ((node.data as any).step as string | undefined) ?? inferStepFromNodeType(node.type),
        params: normalizeWorkflowParams((((node.data as any).params as WorkflowParams | undefined) ?? {})),
        ui: {
          type: node.type,
          position: node.position,
          label: ((node.data as any).label as string | undefined) ?? inferNodeLabel(((node.data as any).step as string | undefined) ?? inferStepFromNodeType(node.type)),
        },
      })),
      edges: edges.map(edge => ({
        from: edge.source,
        to: edge.target,
      })) as WorkflowEdge[],
    }
    onSave(updatedConfig)
  }

  const selectedNode = nodes.find(n => n.id === selectedNodeId)

  const { mode } = useThemeStore()
  const isDark = resolveTheme(mode) === 'dark'

  return (
    <div className="flex flex-col flex-1 min-h-0">
      {/* Canvas Toolbar */}
      <div className="flex items-center gap-2 px-4 py-2 border-b border-border-default bg-surface overflow-x-auto">
        <span className="text-sm font-medium text-content-secondary mr-2 whitespace-nowrap">Nodes</span>
        {nodeDefinitions.map(definition => (
          <div
            key={definition.step}
            draggable
            onDragStart={e => {
              e.dataTransfer.setData('application/workflow-step', definition.step)
              e.dataTransfer.effectAllowed = 'move'
            }}
            className="flex items-center gap-1.5 px-2.5 py-1.5 rounded border border-border-default bg-surface-hover text-xs text-content-secondary cursor-grab hover:bg-surface-muted select-none whitespace-nowrap"
            title={definition.description}
          >
            {renderWorkflowIcon(definition.icon)}
            {definition.label}
          </div>
        ))}
        <div className="ml-auto">
          <button
            onClick={handleSave}
            disabled={isSaving}
            className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg bg-accent text-white hover:bg-accent-hover disabled:opacity-50"
          >
            <Save size={14} />
            {isSaving ? 'Saving…' : 'Save'}
          </button>
        </div>
      </div>

      {/* Canvas + Sidepanel */}
      <div className="flex flex-1 min-h-0">
        <div ref={reactFlowWrapper} className="flex-1" onDrop={onDrop} onDragOver={onDragOver}>
          <ReactFlow
            nodes={nodes}
            edges={edges}
            onNodesChange={onNodesChange}
            onEdgesChange={onEdgesChange}
            onConnect={onConnect}
            onNodeClick={onNodeClick}
            onPaneClick={onPaneClick}
            onInit={setReactFlowInstance}
            nodeTypes={nodeTypes}
            colorMode={isDark ? 'dark' : 'light'}
            fitView
            fitViewOptions={{ padding: 0.2 }}
          >
            <Background gap={16} />
            <Controls />
            <MiniMap nodeStrokeWidth={3} zoomable pannable />
          </ReactFlow>
        </div>

        {selectedNode && (
          <ConfigSidepanel
            params={((selectedNode.data as any).params as WorkflowParams | undefined) ?? {}}
            onChange={handleParamsChange}
            step={(selectedNode.data as any).step as string | undefined}
            onStepChange={handlePipelineStepChange}
            nodeDefinition={nodeDefinitionsByStep[((selectedNode.data as any).step as string | undefined) ?? '']}
            nodeDefinitions={nodeDefinitions}
          />
        )}
        {!selectedNode && nodeDefinitions.length > 0 && (
          <div className="w-64 border-l border-border-default bg-surface p-4 overflow-y-auto">
            <NodeDefinitionsPanel definitions={nodeDefinitions} />
          </div>
        )}
      </div>
    </div>
  )
}

// ─── Main Page ────────────────────────────────────────────────────────────────

export default function WorkflowEditor() {
  const queryClient = useQueryClient()
  const [selectedId, setSelectedId] = useState<string | null>(null)
  const [showNewModal, setShowNewModal] = useState(false)

  const { data: workflows = [], isLoading } = useQuery({
    queryKey: ['workflows'],
    queryFn: getWorkflows,
  })

  const createMutation = useMutation({
    mutationFn: createWorkflow,
    onSuccess: wf => {
      queryClient.invalidateQueries({ queryKey: ['workflows'] })
      setSelectedId(wf.id)
      setShowNewModal(false)
      toast.success('Workflow created')
    },
    onError: () => toast.error('Failed to create workflow'),
  })

  const updateMutation = useMutation({
    mutationFn: ({ id, config }: { id: string; config: WorkflowConfig }) =>
      updateWorkflow(id, { config }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['workflows'] })
      toast.success('Workflow saved')
    },
    onError: () => toast.error('Failed to save workflow'),
  })

  const deleteMutation = useMutation({
    mutationFn: deleteWorkflow,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['workflows'] })
      setSelectedId(null)
      toast.success('Workflow deleted')
    },
    onError: () => toast.error('Failed to delete workflow'),
  })

  const handleCreate = (name: string, type: WorkflowPresetType) => {
    const defaultParams: WorkflowParams =
      type === 'turntable'
        ? { render_engine: 'cycles', samples: 64, fps: 24, duration_s: 5 }
        : type === 'multi_angle'
        ? { render_engine: 'cycles', samples: 128, resolution: [2048, 2048], angles: [0, 45, 90] }
        : { render_engine: 'cycles', samples: 256, resolution: [2048, 2048] }

    createMutation.mutate({
      name,
      config: createPresetWorkflowConfig(type, defaultParams),
      is_active: true,
    })
  }

  const selectedWorkflow = workflows.find(w => w.id === selectedId) ?? null

  const typeLabel: Record<WorkflowPresetType, string> = {
    still: 'Still',
    turntable: 'Turntable',
    multi_angle: 'Multi-Angle',
    still_with_exports: 'Still + GLB',
    custom: 'Custom',
  }

  const typeBadgeColor: Record<WorkflowPresetType, string> = {
    still: 'bg-orange-100 text-orange-700 dark:bg-orange-900/40 dark:text-orange-300',
    turntable: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300',
    multi_angle: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300',
    still_with_exports: 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300',
    custom: 'bg-surface-hover text-content-muted',
  }

  return (
    <div className="flex h-full">
      {/* Workflow List Sidebar */}
      <aside className="w-56 flex-shrink-0 border-r border-border-default bg-surface flex flex-col">
        <div className="p-3 border-b border-border-default flex items-center justify-between">
          <div className="flex items-center gap-2 text-sm font-semibold text-content-secondary">
            <GitBranch size={16} />
            Workflows
          </div>
          <button
            onClick={() => setShowNewModal(true)}
            className="p-1 rounded hover:bg-surface-hover text-content-muted hover:text-content"
            title="New Workflow"
          >
            <Plus size={16} />
          </button>
        </div>

        <div className="flex-1 overflow-y-auto p-2 space-y-1">
          {isLoading && (
            <p className="text-xs text-content-muted px-2 py-4 text-center">Loading…</p>
          )}
          {!isLoading && workflows.length === 0 && (
            <div className="px-2 py-4 text-center">
              <p className="text-xs text-content-secondary font-medium">No workflows configured.</p>
              <p className="text-xs text-content-muted mt-1">
                Workflows define the sequence of pipeline steps for rendering orders.
              </p>
              <button
                onClick={() => setShowNewModal(true)}
                className="mt-2 text-xs text-accent hover:underline"
              >
                + New Workflow
              </button>
            </div>
          )}
          {workflows.map(wf => {
            const presetType = getWorkflowPresetType(wf.config)
            return (
            <button
              key={wf.id}
              onClick={() => setSelectedId(wf.id)}
              className={`w-full text-left px-3 py-2.5 rounded-lg transition-colors group ${
                selectedId === wf.id
                  ? 'bg-accent-light border border-accent/30'
                  : 'hover:bg-surface-hover border border-transparent'
              }`}
            >
              <div className="flex items-start justify-between gap-1">
                <div className="flex items-center gap-1.5 min-w-0">
                  {wf.is_active && (
                    <span
                      className="flex-shrink-0 w-2 h-2 rounded-full bg-green-500"
                      title="Active"
                    />
                  )}
                  <p className="text-sm font-medium text-content truncate">{wf.name}</p>
                </div>
                <button
                  onClick={e => {
                    e.stopPropagation()
                    if (confirm(`Delete workflow "${wf.name}"?`)) {
                      deleteMutation.mutate(wf.id)
                    }
                  }}
                  className="opacity-0 group-hover:opacity-100 p-0.5 rounded hover:bg-red-100 hover:text-red-600 text-content-muted flex-shrink-0"
                  title="Delete"
                >
                  <Trash2 size={12} />
                </button>
              </div>
              <span
                className={`inline-block mt-1 text-xs px-1.5 py-0.5 rounded-full font-medium ${
                  typeBadgeColor[presetType]
                }`}
              >
                {typeLabel[presetType]}
              </span>
              {!wf.is_active && (
                <span className="ml-1 text-xs text-content-muted">(inactive)</span>
              )}
            </button>
            )
          })}
        </div>
      </aside>

      {/* Main Canvas Area */}
      <div className="flex-1 flex flex-col min-w-0">
        {/* Header */}
        <div className="px-6 py-4 border-b border-border-default bg-surface flex items-center justify-between">
          <div>
            <h1 className="text-xl font-semibold text-content">Workflow Editor</h1>
            {selectedWorkflow && (
              <p className="text-sm text-content-muted mt-0.5">{selectedWorkflow.name}</p>
            )}
          </div>
          <button
            onClick={() => setShowNewModal(true)}
            className="flex items-center gap-2 px-4 py-2 rounded-lg bg-accent text-white text-sm font-medium hover:bg-accent-hover transition-colors"
          >
            <Plus size={16} />
            New Workflow
          </button>
        </div>

        {/* Canvas or Empty State */}
        {selectedWorkflow ? (
          <FlowCanvas
            key={selectedWorkflow.id}
            workflow={selectedWorkflow}
            onSave={config => updateMutation.mutate({ id: selectedWorkflow.id, config })}
            isSaving={updateMutation.isPending}
          />
        ) : (
          <div className="flex-1 flex items-center justify-center text-center">
            <div>
              <GitBranch size={48} className="mx-auto text-content-muted mb-4" />
              {workflows.length === 0 ? (
                <>
                  <p className="text-content-secondary font-medium">No workflows configured.</p>
                  <p className="text-sm text-content-muted mt-1 max-w-xs mx-auto">
                    Workflows define the sequence of pipeline steps for rendering orders.
                    Click "New Workflow" to create one.
                  </p>
                </>
              ) : (
                <>
                  <p className="text-content-secondary font-medium">No workflow selected</p>
                  <p className="text-sm text-content-muted mt-1">
                    Select a workflow from the list or create a new one.
                  </p>
                </>
              )}
              <button
                onClick={() => setShowNewModal(true)}
                className="mt-4 flex items-center gap-2 px-4 py-2 mx-auto rounded-lg bg-accent text-white text-sm font-medium hover:bg-accent-hover"
              >
                <Plus size={16} />
                New Workflow
              </button>
            </div>
          </div>
        )}
      </div>

      {/* New Workflow Modal */}
      {showNewModal && (
        <NewWorkflowModal
          onClose={() => setShowNewModal(false)}
          onCreate={handleCreate}
          isLoading={createMutation.isPending}
        />
      )}
    </div>
  )
}
