'use client';

import { useState, useCallback, useMemo } from 'react';
import type { ScenarioJson, ScenarioNodeJson } from './scenario-converter';

// ─── Types ──────────────────────────────────────────────────────

interface StepEditorProps {
  scenario: ScenarioJson;
  onChange: (scenario: ScenarioJson) => void;
}

interface ButtonData {
  text: string;
  action: string;
  target?: string;
  url?: string;
}

interface ConditionRule {
  condition: string;
  target: string;
}

interface ActionItem {
  type: string;
  variable?: string;
  value?: unknown;
}

// ─── Color config per node type ─────────────────────────────────

const NODE_COLORS: Record<
  string,
  { bg: string; border: string; header: string; headerBorder: string }
> = {
  message: { bg: '#fff8e1', border: '#ffb300', header: '#e65100', headerBorder: '#ffe082' },
  condition: { bg: '#fff3e0', border: '#ff9800', header: '#e65100', headerBorder: '#ffe0b2' },
  action: { bg: '#f3e5f5', border: '#ab47bc', header: '#6a1b9a', headerBorder: '#ce93d8' },
  input: { bg: '#e0f2f1', border: '#26a69a', header: '#004d40', headerBorder: '#80cbc4' },
  delay: { bg: '#fbe9e7', border: '#ff7043', header: '#bf360c', headerBorder: '#ffab91' },
  referral: { bg: '#fce4ec', border: '#ec407a', header: '#880e4f', headerBorder: '#f48fb1' },
};

const NODE_LABELS: Record<string, { icon: string; label: string }> = {
  message: { icon: '\uD83D\uDCAC', label: 'Сообщение' },
  condition: { icon: '\uD83D\uDD00', label: 'Условие' },
  action: { icon: '\u26A1', label: 'Действие' },
  input: { icon: '\u270F\uFE0F', label: 'Ввод данных' },
  delay: { icon: '\u23F0', label: 'Задержка' },
  referral: { icon: '\uD83C\uDF81', label: 'Реферал' },
};

const ACTION_TYPES = [
  { value: 'set_variable', label: 'Переменная' },
  { value: 'grant_bonus', label: 'Бонус' },
  { value: 'set_consent', label: 'Согласие' },
  { value: 'crm_deal', label: 'Сделка CRM' },
  { value: 'webhook', label: 'Webhook' },
];

const INPUT_TYPES = [
  { value: 'text', label: 'Текст' },
  { value: 'phone', label: 'Телефон' },
  { value: 'email', label: 'Email' },
  { value: 'number', label: 'Число' },
];

const NODE_TYPE_OPTIONS = [
  { value: 'message', label: 'Сообщение' },
  { value: 'condition', label: 'Условие' },
  { value: 'action', label: 'Действие' },
  { value: 'input', label: 'Ввод данных' },
  { value: 'delay', label: 'Задержка' },
  { value: 'referral', label: 'Реферал' },
];

// ─── Helpers ────────────────────────────────────────────────────

function generateNodeId(type: string, existingIds: string[]): string {
  const base = type;
  let counter = 1;
  while (existingIds.includes(`${base}_${counter}`)) counter++;
  return `${base}_${counter}`;
}

function createDefaultNode(type: string, id: string): ScenarioNodeJson {
  const base: ScenarioNodeJson = { id, type, data: {}, next: null };
  switch (type) {
    case 'message':
      return { ...base, data: { text: '', buttons: [] } };
    case 'condition':
      return { ...base, data: { rules: [], default: null } };
    case 'action':
      return { ...base, data: { actions: [] } };
    case 'input':
      return { ...base, data: { prompt: '', inputType: 'text', saveAs: '' } };
    case 'delay':
      return { ...base, data: { duration: '30m', cancelOn: '' } };
    case 'referral':
      return {
        ...base,
        data: {
          targetCount: 3,
          trackVariable: 'invited_count',
          onReach: null,
          friendStartNode: null,
        },
      };
    default:
      return base;
  }
}

/** Walk `next` chain from a starting node, collecting visited node IDs in order. */
function walkNextChain(
  startId: string,
  nodes: Record<string, ScenarioNodeJson>,
  visited: Set<string>,
): string[] {
  const chain: string[] = [];
  let currentId: string | null | undefined = startId;
  while (currentId && nodes[currentId] && !visited.has(currentId)) {
    visited.add(currentId);
    chain.push(currentId);
    currentId = nodes[currentId]!.next;
  }
  return chain;
}

// ─── Vertical Arrow ─────────────────────────────────────────────

function VerticalArrow() {
  return (
    <div className="flex flex-col items-center py-1" style={{ height: 32 }}>
      <div style={{ width: 2, flex: 1, backgroundColor: '#C0C1C3' }} />
      <div
        style={{
          width: 0,
          height: 0,
          borderLeft: '5px solid transparent',
          borderRight: '5px solid transparent',
          borderTop: '6px solid #C0C1C3',
        }}
      />
    </div>
  );
}

// ─── NodeTargetSelect ───────────────────────────────────────────

function NodeTargetSelect({
  value,
  nodeIds,
  nodes,
  onChange,
  className,
}: {
  value: string | null | undefined;
  nodeIds: string[];
  nodes: Record<string, ScenarioNodeJson>;
  onChange: (val: string | null) => void;
  className?: string;
}) {
  return (
    <select
      value={value ?? ''}
      onChange={(e) => onChange(e.target.value || null)}
      className={`rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700 ${className ?? ''}`}
    >
      <option value="">&mdash; нет &mdash;</option>
      {nodeIds.map((nid) => {
        const n = nodes[nid];
        const typeLabel = n ? n.type : '?';
        return (
          <option key={nid} value={nid}>
            {nid} ({typeLabel})
          </option>
        );
      })}
    </select>
  );
}

// ─── Node Card Components ───────────────────────────────────────

function MessageCard({
  node,
  allNodeIds,
  allNodes,
  onUpdate,
}: {
  node: ScenarioNodeJson;
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  isStart: boolean;
  onUpdate: (data: Record<string, unknown>, next?: string | null) => void;
  onDelete: () => void;
}) {
  const text = (node.data.text ?? '') as string;
  const image = (node.data.image ?? '') as string;
  const buttons = (node.data.buttons ?? []) as ButtonData[];

  const updateButton = (index: number, patch: Partial<ButtonData>) => {
    const newButtons = buttons.map((b, i) => (i === index ? { ...b, ...patch } : b));
    onUpdate({ ...node.data, buttons: newButtons });
  };

  const addButton = () => {
    const newButtons = [...buttons, { text: '', action: 'goto', target: '' }];
    onUpdate({ ...node.data, buttons: newButtons });
  };

  const removeButton = (index: number) => {
    const newButtons = buttons.filter((_, i) => i !== index);
    onUpdate({ ...node.data, buttons: newButtons });
  };

  return (
    <div className="space-y-3">
      {/* Text */}
      <div>
        <label className="block text-xs font-medium text-gray-600 mb-1">Текст:</label>
        <textarea
          value={text}
          onChange={(e) => onUpdate({ ...node.data, text: e.target.value })}
          className="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-800 resize-y focus:outline-none focus:ring-2 focus:ring-amber-300"
          style={{ minHeight: 80 }}
          placeholder="Текст сообщения..."
        />
      </div>

      {/* Image */}
      <div>
        <label className="block text-xs font-medium text-gray-600 mb-1">Картинка:</label>
        <input
          type="text"
          value={image}
          onChange={(e) => onUpdate({ ...node.data, image: e.target.value })}
          className="w-full rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm text-gray-800 focus:outline-none focus:ring-2 focus:ring-amber-300"
          placeholder="URL картинки или путь..."
        />
      </div>

      {/* Buttons */}
      <div>
        <label className="block text-xs font-medium text-gray-600 mb-1">Кнопки:</label>
        <div className="space-y-2">
          {buttons.map((btn, i) => (
            <div key={i} className="rounded-lg border border-gray-200 bg-gray-50 p-2.5 space-y-1.5">
              <div className="flex items-center gap-2">
                <span className="text-xs text-gray-400 shrink-0">#{i + 1}</span>
                <input
                  type="text"
                  value={btn.text}
                  onChange={(e) => updateButton(i, { text: e.target.value })}
                  className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-300"
                  placeholder="Текст кнопки"
                />
                <button
                  onClick={() => removeButton(i)}
                  className="text-red-400 hover:text-red-600 text-sm shrink-0"
                  title="Удалить кнопку"
                >
                  &#128465;
                </button>
              </div>
              <div className="flex items-center gap-2">
                <select
                  value={btn.action}
                  onChange={(e) => updateButton(i, { action: e.target.value })}
                  className="rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700"
                >
                  <option value="goto">goto</option>
                  <option value="url">url</option>
                  <option value="share_contact">share_contact</option>
                </select>
                {btn.action === 'goto' && (
                  <NodeTargetSelect
                    value={btn.target ?? null}
                    nodeIds={allNodeIds}
                    nodes={allNodes}
                    onChange={(val) => updateButton(i, { target: val ?? '' })}
                    className="flex-1"
                  />
                )}
                {btn.action === 'url' && (
                  <input
                    type="text"
                    value={btn.url ?? ''}
                    onChange={(e) => updateButton(i, { url: e.target.value })}
                    className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-800 focus:outline-none"
                    placeholder="https://..."
                  />
                )}
              </div>
            </div>
          ))}
        </div>
        <button
          onClick={addButton}
          className="mt-2 rounded-lg border border-dashed border-gray-300 px-3 py-1.5 text-xs text-gray-500 hover:border-gray-400 hover:text-gray-700 transition-colors"
        >
          + Добавить кнопку
        </button>
      </div>

      {/* Next */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Далее &rarr;</span>
        <NodeTargetSelect
          value={node.next}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate(node.data, val)}
          className="flex-1"
        />
      </div>
    </div>
  );
}

function ConditionCard({
  node,
  allNodeIds,
  allNodes,
  onUpdate,
}: {
  node: ScenarioNodeJson;
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  isStart: boolean;
  onUpdate: (data: Record<string, unknown>, next?: string | null) => void;
  onDelete: () => void;
}) {
  const rules = (node.data.rules ?? []) as ConditionRule[];
  const defaultTarget = (node.data.default ?? null) as string | null;

  const updateRule = (index: number, patch: Partial<ConditionRule>) => {
    const newRules = rules.map((r, i) => (i === index ? { ...r, ...patch } : r));
    onUpdate({ ...node.data, rules: newRules });
  };

  const addRule = () => {
    const newRules = [...rules, { condition: '', target: '' }];
    onUpdate({ ...node.data, rules: newRules });
  };

  const removeRule = (index: number) => {
    const newRules = rules.filter((_, i) => i !== index);
    onUpdate({ ...node.data, rules: newRules });
  };

  return (
    <div className="space-y-3">
      {/* Rules */}
      <div>
        <label className="block text-xs font-medium text-gray-600 mb-1">Правила:</label>
        <div className="space-y-2">
          {rules.map((rule, i) => (
            <div key={i} className="rounded-lg border border-gray-200 bg-gray-50 p-2.5 space-y-1.5">
              <div className="flex items-center gap-2">
                <span className="text-xs text-orange-600 font-semibold shrink-0">если</span>
                <input
                  type="text"
                  value={rule.condition}
                  onChange={(e) => updateRule(i, { condition: e.target.value })}
                  className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs font-mono text-gray-800 focus:outline-none focus:ring-2 focus:ring-orange-300"
                  placeholder="Условие..."
                />
                <button
                  onClick={() => removeRule(i)}
                  className="text-red-400 hover:text-red-600 text-sm shrink-0"
                  title="Удалить правило"
                >
                  &#128465;
                </button>
              </div>
              <div className="flex items-center gap-2">
                <span className="text-xs text-gray-500 shrink-0">&rarr;</span>
                <NodeTargetSelect
                  value={rule.target}
                  nodeIds={allNodeIds}
                  nodes={allNodes}
                  onChange={(val) => updateRule(i, { target: val ?? '' })}
                  className="flex-1"
                />
              </div>
            </div>
          ))}
        </div>
        <button
          onClick={addRule}
          className="mt-2 rounded-lg border border-dashed border-gray-300 px-3 py-1.5 text-xs text-gray-500 hover:border-gray-400 hover:text-gray-700 transition-colors"
        >
          + Добавить правило
        </button>
      </div>

      {/* Default */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Иначе &rarr;</span>
        <NodeTargetSelect
          value={defaultTarget}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate({ ...node.data, default: val })}
          className="flex-1"
        />
      </div>

      {/* Next */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Далее &rarr;</span>
        <NodeTargetSelect
          value={node.next}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate(node.data, val)}
          className="flex-1"
        />
      </div>
    </div>
  );
}

function ActionCard({
  node,
  allNodeIds,
  allNodes,
  onUpdate,
}: {
  node: ScenarioNodeJson;
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  isStart: boolean;
  onUpdate: (data: Record<string, unknown>, next?: string | null) => void;
  onDelete: () => void;
}) {
  const actions = (node.data.actions ?? []) as ActionItem[];

  const updateAction = (index: number, patch: Partial<ActionItem>) => {
    const newActions = actions.map((a, i) => (i === index ? { ...a, ...patch } : a));
    onUpdate({ ...node.data, actions: newActions });
  };

  const addAction = () => {
    const newActions = [...actions, { type: 'set_variable', variable: '', value: '' }];
    onUpdate({ ...node.data, actions: newActions });
  };

  const removeAction = (index: number) => {
    const newActions = actions.filter((_, i) => i !== index);
    onUpdate({ ...node.data, actions: newActions });
  };

  return (
    <div className="space-y-3">
      {/* Actions */}
      <div>
        <label className="block text-xs font-medium text-gray-600 mb-1">Действия:</label>
        <div className="space-y-2">
          {actions.map((action, i) => (
            <div key={i} className="rounded-lg border border-gray-200 bg-gray-50 p-2.5 space-y-1.5">
              <div className="flex items-center gap-2">
                <select
                  value={action.type}
                  onChange={(e) => updateAction(i, { type: e.target.value })}
                  className="rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700"
                >
                  {ACTION_TYPES.map((at) => (
                    <option key={at.value} value={at.value}>
                      {at.label}
                    </option>
                  ))}
                </select>
                <input
                  type="text"
                  value={action.variable ?? ''}
                  onChange={(e) => updateAction(i, { variable: e.target.value })}
                  className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs font-mono text-gray-800 focus:outline-none"
                  placeholder="Переменная"
                />
                <button
                  onClick={() => removeAction(i)}
                  className="text-red-400 hover:text-red-600 text-sm shrink-0"
                  title="Удалить действие"
                >
                  &#128465;
                </button>
              </div>
              <div className="flex items-center gap-2">
                <span className="text-xs text-gray-500 shrink-0">=</span>
                <input
                  type="text"
                  value={
                    action.value !== undefined
                      ? String(action.value as string | number | boolean)
                      : ''
                  }
                  onChange={(e) => updateAction(i, { value: e.target.value })}
                  className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-800 focus:outline-none"
                  placeholder="Значение"
                />
              </div>
            </div>
          ))}
        </div>
        <button
          onClick={addAction}
          className="mt-2 rounded-lg border border-dashed border-gray-300 px-3 py-1.5 text-xs text-gray-500 hover:border-gray-400 hover:text-gray-700 transition-colors"
        >
          + Добавить действие
        </button>
      </div>

      {/* Next */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Далее &rarr;</span>
        <NodeTargetSelect
          value={node.next}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate(node.data, val)}
          className="flex-1"
        />
      </div>
    </div>
  );
}

function InputCard({
  node,
  allNodeIds,
  allNodes,
  onUpdate,
}: {
  node: ScenarioNodeJson;
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  isStart: boolean;
  onUpdate: (data: Record<string, unknown>, next?: string | null) => void;
  onDelete: () => void;
}) {
  const prompt = (node.data.prompt ?? '') as string;
  const inputType = (node.data.inputType ?? 'text') as string;
  const saveAs = (node.data.saveAs ?? '') as string;

  return (
    <div className="space-y-3">
      {/* Prompt */}
      <div>
        <label className="block text-xs font-medium text-gray-600 mb-1">Запрос:</label>
        <textarea
          value={prompt}
          onChange={(e) => onUpdate({ ...node.data, prompt: e.target.value })}
          className="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-800 resize-y focus:outline-none focus:ring-2 focus:ring-teal-300"
          style={{ minHeight: 60 }}
          placeholder="Текст запроса пользователю..."
        />
      </div>

      {/* Input type */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Тип ввода:</span>
        <select
          value={inputType}
          onChange={(e) => onUpdate({ ...node.data, inputType: e.target.value })}
          className="rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700"
        >
          {INPUT_TYPES.map((it) => (
            <option key={it.value} value={it.value}>
              {it.label}
            </option>
          ))}
        </select>
      </div>

      {/* Save as */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600 shrink-0">Сохранить как:</span>
        <input
          type="text"
          value={saveAs}
          onChange={(e) => onUpdate({ ...node.data, saveAs: e.target.value })}
          className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs font-mono text-gray-800 focus:outline-none"
          placeholder="имя_переменной"
        />
      </div>

      {/* Next */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Далее &rarr;</span>
        <NodeTargetSelect
          value={node.next}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate(node.data, val)}
          className="flex-1"
        />
      </div>
    </div>
  );
}

function DelayCard({
  node,
  allNodeIds,
  allNodes,
  onUpdate,
}: {
  node: ScenarioNodeJson;
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  isStart: boolean;
  onUpdate: (data: Record<string, unknown>, next?: string | null) => void;
  onDelete: () => void;
}) {
  const duration = (node.data.duration ?? '') as string;
  const cancelOn = (node.data.cancelOn ?? '') as string;

  return (
    <div className="space-y-3">
      {/* Duration */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600 shrink-0">Длительность:</span>
        <input
          type="text"
          value={duration}
          onChange={(e) => onUpdate({ ...node.data, duration: e.target.value })}
          className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-sm font-mono text-gray-800 focus:outline-none"
          placeholder="30m, 1h, 1d..."
        />
      </div>

      {/* Cancel condition */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600 shrink-0">Отмена при:</span>
        <input
          type="text"
          value={cancelOn}
          onChange={(e) => onUpdate({ ...node.data, cancelOn: e.target.value })}
          className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-800 focus:outline-none"
          placeholder="Условие отмены..."
        />
      </div>

      {/* Next */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Далее &rarr;</span>
        <NodeTargetSelect
          value={node.next}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate(node.data, val)}
          className="flex-1"
        />
      </div>
    </div>
  );
}

function ReferralCard({
  node,
  allNodeIds,
  allNodes,
  onUpdate,
}: {
  node: ScenarioNodeJson;
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  isStart: boolean;
  onUpdate: (data: Record<string, unknown>, next?: string | null) => void;
  onDelete: () => void;
}) {
  const targetCount = (node.data.targetCount ?? 0) as number;
  const trackVariable = (node.data.trackVariable ?? 'invited_count') as string;
  const onReach = (node.data.onReach ?? null) as string | null;
  const friendStartNode = (node.data.friendStartNode ?? null) as string | null;

  return (
    <div className="space-y-3">
      {/* Target count */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600 shrink-0">Цель (друзей):</span>
        <input
          type="number"
          value={targetCount}
          onChange={(e) => onUpdate({ ...node.data, targetCount: parseInt(e.target.value) || 0 })}
          className="w-20 rounded border border-gray-300 bg-white px-2 py-1 text-sm text-gray-800 focus:outline-none"
          min={0}
        />
      </div>

      {/* Track variable */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600 shrink-0">Переменная:</span>
        <input
          type="text"
          value={trackVariable}
          onChange={(e) => onUpdate({ ...node.data, trackVariable: e.target.value })}
          className="flex-1 rounded border border-gray-300 bg-white px-2 py-1 text-xs font-mono text-gray-800 focus:outline-none"
          placeholder="invited_count"
        />
      </div>

      {/* On reach target */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600 shrink-0">При достижении &rarr;</span>
        <NodeTargetSelect
          value={onReach}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate({ ...node.data, onReach: val })}
          className="flex-1"
        />
      </div>

      {/* Friend start node */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600 shrink-0">Нода для друга &rarr;</span>
        <NodeTargetSelect
          value={friendStartNode}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate({ ...node.data, friendStartNode: val })}
          className="flex-1"
        />
      </div>

      {/* Next */}
      <div className="flex items-center gap-2">
        <span className="text-xs font-medium text-gray-600">Далее &rarr;</span>
        <NodeTargetSelect
          value={node.next}
          nodeIds={allNodeIds}
          nodes={allNodes}
          onChange={(val) => onUpdate(node.data, val)}
          className="flex-1"
        />
      </div>
    </div>
  );
}

// ─── Unified Node Card ──────────────────────────────────────────

function NodeCard({
  node,
  allNodeIds,
  allNodes,
  isStart,
  onUpdate,
  onDelete,
}: {
  node: ScenarioNodeJson;
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  isStart: boolean;
  onUpdate: (data: Record<string, unknown>, next?: string | null) => void;
  onDelete: () => void;
}) {
  const colors = NODE_COLORS[node.type] ?? NODE_COLORS.message!;
  const labels = NODE_LABELS[node.type] ?? { icon: '?', label: node.type };
  const [confirmDelete, setConfirmDelete] = useState(false);

  const handleDelete = () => {
    if (confirmDelete) {
      onDelete();
    } else {
      setConfirmDelete(true);
      setTimeout(() => setConfirmDelete(false), 3000);
    }
  };

  const cardProps = { node, allNodeIds, allNodes, isStart, onUpdate, onDelete };

  return (
    <div
      className="rounded-xl border-2 shadow-sm overflow-hidden"
      style={{ borderColor: colors.border, backgroundColor: colors.bg }}
    >
      {/* Header */}
      <div
        className="flex items-center gap-2 px-4 py-2.5"
        style={{ borderBottom: `1px solid ${colors.headerBorder}` }}
      >
        <span className="text-base">{labels.icon}</span>
        <span className="text-sm font-bold" style={{ color: colors.header }}>
          {labels.label}
        </span>
        <span className="text-xs text-gray-400 font-mono ml-1">{node.id}</span>
        {isStart && (
          <span
            className="ml-auto mr-2 rounded-full px-2 py-0.5 text-xs font-semibold"
            style={{ color: '#2e7d32', backgroundColor: '#e8f5e9' }}
          >
            START
          </span>
        )}
        <div className="ml-auto flex items-center gap-1">
          <button
            onClick={handleDelete}
            className={`rounded px-2 py-1 text-xs transition-colors ${
              confirmDelete
                ? 'bg-red-500 text-white'
                : 'text-gray-400 hover:text-red-500 hover:bg-red-50'
            }`}
            title={confirmDelete ? 'Нажмите ещё раз для удаления' : 'Удалить шаг'}
          >
            {confirmDelete ? 'Точно удалить?' : '\uD83D\uDDD1'}
          </button>
        </div>
      </div>

      {/* Body */}
      <div className="px-4 py-3">
        {node.type === 'message' && <MessageCard {...cardProps} />}
        {node.type === 'condition' && <ConditionCard {...cardProps} />}
        {node.type === 'action' && <ActionCard {...cardProps} />}
        {node.type === 'input' && <InputCard {...cardProps} />}
        {node.type === 'delay' && <DelayCard {...cardProps} />}
        {node.type === 'referral' && <ReferralCard {...cardProps} />}
      </div>
    </div>
  );
}

// ─── Branch Section (collapsible) ───────────────────────────────

function BranchSection({
  branchStartId,
  chain,
  allNodeIds,
  allNodes,
  startNodeId,
  onUpdateNode,
  onDeleteNode,
}: {
  branchStartId: string;
  chain: string[];
  allNodeIds: string[];
  allNodes: Record<string, ScenarioNodeJson>;
  startNodeId: string;
  onUpdateNode: (nodeId: string, data: Record<string, unknown>, next?: string | null) => void;
  onDeleteNode: (nodeId: string) => void;
}) {
  const [expanded, setExpanded] = useState(false);
  const firstNode = allNodes[branchStartId];
  const typeLabel = firstNode ? (NODE_LABELS[firstNode.type]?.label ?? firstNode.type) : '?';

  return (
    <div className="rounded-lg border border-gray-200 bg-white overflow-hidden">
      <button
        onClick={() => setExpanded(!expanded)}
        className="w-full flex items-center gap-3 px-4 py-2.5 text-left hover:bg-gray-50 transition-colors"
      >
        <span
          className="text-sm"
          style={{
            transform: expanded ? 'rotate(90deg)' : 'rotate(0)',
            transition: 'transform 0.15s',
          }}
        >
          &#9654;
        </span>
        <span className="text-sm font-medium text-gray-800">{branchStartId}</span>
        <span className="text-xs text-gray-400">({typeLabel})</span>
        <span className="text-xs text-gray-400 ml-auto">
          {chain.length} {chain.length === 1 ? 'шаг' : chain.length < 5 ? 'шага' : 'шагов'}
        </span>
      </button>
      {expanded && (
        <div className="px-4 pb-4 border-t border-gray-100">
          <div className="pt-3 space-y-0">
            {chain.map((nodeId, i) => {
              const node = allNodes[nodeId];
              if (!node) return null;
              return (
                <div key={nodeId}>
                  {i > 0 && <VerticalArrow />}
                  <NodeCard
                    node={node}
                    allNodeIds={allNodeIds}
                    allNodes={allNodes}
                    isStart={nodeId === startNodeId}
                    onUpdate={(data, next) => onUpdateNode(nodeId, data, next)}
                    onDelete={() => onDeleteNode(nodeId)}
                  />
                </div>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

// ─── Add Step Picker ────────────────────────────────────────────

function AddStepPicker({ onAdd }: { onAdd: (type: string) => void }) {
  const [open, setOpen] = useState(false);

  if (!open) {
    return (
      <button
        onClick={() => setOpen(true)}
        className="w-full rounded-xl border-2 border-dashed border-gray-300 py-3 text-sm text-gray-500 hover:border-gray-400 hover:text-gray-700 transition-colors"
      >
        + Добавить шаг
      </button>
    );
  }

  return (
    <div className="rounded-xl border border-gray-200 bg-white p-3 shadow-sm">
      <p className="text-xs font-medium text-gray-500 mb-2">Выберите тип шага:</p>
      <div className="flex flex-wrap gap-2">
        {NODE_TYPE_OPTIONS.map((opt) => {
          const labels = NODE_LABELS[opt.value]!;
          const colors = NODE_COLORS[opt.value]!;
          return (
            <button
              key={opt.value}
              onClick={() => {
                onAdd(opt.value);
                setOpen(false);
              }}
              className="flex items-center gap-1.5 rounded-lg px-3 py-2 text-xs font-medium border transition-colors hover:shadow-sm"
              style={{
                borderColor: colors.border,
                backgroundColor: colors.bg,
                color: colors.header,
              }}
            >
              <span>{labels.icon}</span> {labels.label}
            </button>
          );
        })}
      </div>
      <button
        onClick={() => setOpen(false)}
        className="mt-2 text-xs text-gray-400 hover:text-gray-600"
      >
        Отмена
      </button>
    </div>
  );
}

// ─── Main Component ─────────────────────────────────────────────

export function StepEditor({ scenario, onChange }: StepEditorProps) {
  const allNodeIds = useMemo(() => Object.keys(scenario.nodes), [scenario.nodes]);

  // Compute main flow chain + sidebar branches + orphans
  const { mainChain, sidebarBranches, orphanIds } = useMemo(() => {
    const visited = new Set<string>();
    const main = walkNextChain(scenario.startNodeId, scenario.nodes, visited);

    // Collect all branch targets from main flow nodes
    const branchEntries: Array<{ id: string; chain: string[] }> = [];

    // Walk all visited nodes (main chain) and find their branch targets
    const processedBranchRoots = new Set<string>();

    function collectBranches(sourceChain: string[]) {
      for (const nodeId of sourceChain) {
        const node = scenario.nodes[nodeId];
        if (!node) continue;

        // Message buttons with goto targets
        if (node.type === 'message') {
          const buttons = (node.data.buttons ?? []) as ButtonData[];
          for (const btn of buttons) {
            if (
              btn.action === 'goto' &&
              btn.target &&
              !visited.has(btn.target) &&
              !processedBranchRoots.has(btn.target)
            ) {
              processedBranchRoots.add(btn.target);
              const chain = walkNextChain(btn.target, scenario.nodes, visited);
              if (chain.length > 0) {
                branchEntries.push({ id: btn.target, chain });
                collectBranches(chain);
              }
            }
          }
        }

        // Condition rule targets + default
        if (node.type === 'condition') {
          const rules = (node.data.rules ?? []) as ConditionRule[];
          for (const rule of rules) {
            if (
              rule.target &&
              !visited.has(rule.target) &&
              !processedBranchRoots.has(rule.target)
            ) {
              processedBranchRoots.add(rule.target);
              const chain = walkNextChain(rule.target, scenario.nodes, visited);
              if (chain.length > 0) {
                branchEntries.push({ id: rule.target, chain });
                collectBranches(chain);
              }
            }
          }
          const def = node.data.default as string | undefined;
          if (def && !visited.has(def) && !processedBranchRoots.has(def)) {
            processedBranchRoots.add(def);
            const chain = walkNextChain(def, scenario.nodes, visited);
            if (chain.length > 0) {
              branchEntries.push({ id: def, chain });
              collectBranches(chain);
            }
          }
        }

        // Referral onReach + friendStartNode
        if (node.type === 'referral') {
          const onReach = node.data.onReach as string | undefined;
          const friendStart = node.data.friendStartNode as string | undefined;
          for (const target of [onReach, friendStart]) {
            if (target && !visited.has(target) && !processedBranchRoots.has(target)) {
              processedBranchRoots.add(target);
              const chain = walkNextChain(target, scenario.nodes, visited);
              if (chain.length > 0) {
                branchEntries.push({ id: target, chain });
                collectBranches(chain);
              }
            }
          }
        }
      }
    }

    collectBranches(main);

    // Orphans: nodes not visited at all
    const orphans = allNodeIds.filter((id) => !visited.has(id));

    return { mainChain: main, sidebarBranches: branchEntries, orphanIds: orphans };
  }, [scenario, allNodeIds]);

  // ─── Immutable update helpers ───────────────────────────────

  const updateNode = useCallback(
    (nodeId: string, data: Record<string, unknown>, next?: string | null) => {
      const node = scenario.nodes[nodeId];
      if (!node) return;

      const updatedNode: ScenarioNodeJson = {
        ...node,
        data,
        next: next !== undefined ? next : node.next,
      };

      const updatedNodes = { ...scenario.nodes, [nodeId]: updatedNode };
      onChange({ ...scenario, nodes: updatedNodes });
    },
    [scenario, onChange],
  );

  const deleteNode = useCallback(
    (nodeId: string) => {
      const updatedNodes = { ...scenario.nodes };
      delete updatedNodes[nodeId];

      // Clean up references to deleted node
      for (const [id, node] of Object.entries(updatedNodes)) {
        let changed = false;
        let updatedData = { ...node.data };
        let updatedNext = node.next;

        // Clean next reference
        if (node.next === nodeId) {
          updatedNext = null;
          changed = true;
        }

        // Clean message button targets
        if (node.type === 'message') {
          const buttons = (node.data.buttons ?? []) as ButtonData[];
          const newButtons = buttons.map((btn) =>
            btn.target === nodeId ? { ...btn, target: '' } : btn,
          );
          if (JSON.stringify(newButtons) !== JSON.stringify(buttons)) {
            updatedData = { ...updatedData, buttons: newButtons };
            changed = true;
          }
        }

        // Clean condition targets
        if (node.type === 'condition') {
          const rules = (node.data.rules ?? []) as ConditionRule[];
          const newRules = rules.map((r) => (r.target === nodeId ? { ...r, target: '' } : r));
          if (JSON.stringify(newRules) !== JSON.stringify(rules)) {
            updatedData = { ...updatedData, rules: newRules };
            changed = true;
          }
          if (node.data.default === nodeId) {
            updatedData = { ...updatedData, default: null };
            changed = true;
          }
        }

        // Clean referral targets
        if (node.type === 'referral') {
          if (node.data.onReach === nodeId) {
            updatedData = { ...updatedData, onReach: null };
            changed = true;
          }
          if (node.data.friendStartNode === nodeId) {
            updatedData = { ...updatedData, friendStartNode: null };
            changed = true;
          }
        }

        if (changed) {
          updatedNodes[id] = { ...node, data: updatedData, next: updatedNext };
        }
      }

      // Update startNodeId if deleted
      const updatedScenario: ScenarioJson = {
        ...scenario,
        nodes: updatedNodes,
        startNodeId:
          scenario.startNodeId === nodeId
            ? (Object.keys(updatedNodes)[0] ?? '')
            : scenario.startNodeId,
      };

      onChange(updatedScenario);
    },
    [scenario, onChange],
  );

  const addNode = useCallback(
    (type: string) => {
      const newId = generateNodeId(type, allNodeIds);
      const newNode = createDefaultNode(type, newId);
      const updatedNodes = { ...scenario.nodes, [newId]: newNode };
      onChange({ ...scenario, nodes: updatedNodes });
    },
    [scenario, onChange, allNodeIds],
  );

  const nodeCount = allNodeIds.length;

  return (
    <div className="w-full max-w-2xl mx-auto">
      {/* Header */}
      <div className="flex items-center justify-between mb-4 px-1">
        <div className="text-sm text-gray-500">
          {nodeCount} {nodeCount === 1 ? 'нода' : nodeCount < 5 ? 'ноды' : 'нод'}
          {' | '}
          Старт: <span className="font-mono text-gray-700">{scenario.startNodeId}</span>
        </div>
      </div>

      {/* Main Flow */}
      {mainChain.length > 0 && (
        <div className="mb-6">
          <div className="flex items-center gap-2 mb-3 px-1">
            <div className="h-px flex-1 bg-gray-200" />
            <span className="text-xs font-semibold text-gray-400 uppercase tracking-wider">
              Основной поток
            </span>
            <div className="h-px flex-1 bg-gray-200" />
          </div>

          <div>
            {mainChain.map((nodeId, i) => {
              const node = scenario.nodes[nodeId];
              if (!node) return null;
              return (
                <div key={nodeId}>
                  {i > 0 && <VerticalArrow />}
                  <NodeCard
                    node={node}
                    allNodeIds={allNodeIds}
                    allNodes={scenario.nodes}
                    isStart={nodeId === scenario.startNodeId}
                    onUpdate={(data, next) => updateNode(nodeId, data, next)}
                    onDelete={() => deleteNode(nodeId)}
                  />
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* Sidebar Branches */}
      {sidebarBranches.length > 0 && (
        <div className="mb-6">
          <div className="flex items-center gap-2 mb-3 px-1">
            <div className="h-px flex-1 bg-gray-200" />
            <span className="text-xs font-semibold text-gray-400 uppercase tracking-wider">
              Боковые ветки
            </span>
            <div className="h-px flex-1 bg-gray-200" />
          </div>

          <div className="space-y-2">
            {sidebarBranches.map((branch) => (
              <BranchSection
                key={branch.id}
                branchStartId={branch.id}
                chain={branch.chain}
                allNodeIds={allNodeIds}
                allNodes={scenario.nodes}
                startNodeId={scenario.startNodeId}
                onUpdateNode={updateNode}
                onDeleteNode={deleteNode}
              />
            ))}
          </div>
        </div>
      )}

      {/* Orphans */}
      {orphanIds.length > 0 && (
        <div className="mb-6">
          <div className="flex items-center gap-2 mb-3 px-1">
            <div className="h-px flex-1 bg-gray-200" />
            <span className="text-xs font-semibold text-gray-400 uppercase tracking-wider">
              Несвязанные ноды
            </span>
            <div className="h-px flex-1 bg-gray-200" />
          </div>

          <div className="space-y-3">
            {orphanIds.map((nodeId) => {
              const node = scenario.nodes[nodeId];
              if (!node) return null;
              return (
                <NodeCard
                  key={nodeId}
                  node={node}
                  allNodeIds={allNodeIds}
                  allNodes={scenario.nodes}
                  isStart={nodeId === scenario.startNodeId}
                  onUpdate={(data, next) => updateNode(nodeId, data, next)}
                  onDelete={() => deleteNode(nodeId)}
                />
              );
            })}
          </div>
        </div>
      )}

      {/* Add Step */}
      <div className="mt-4">
        <AddStepPicker onAdd={addNode} />
      </div>
    </div>
  );
}
