'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  ReactFlow,
  Background,
  Controls,
  MiniMap,
  MarkerType,
  type Node,
  type Edge,
  type NodeTypes,
  type OnNodesChange,
  type OnEdgesChange,
  applyNodeChanges,
  applyEdgeChanges,
  type NodeMouseHandler,
  BackgroundVariant,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';

import type { ScenarioJson } from './scenario-converter';
import { scenarioToFlow, extractPositions } from './scenario-converter';
import { MessageNode } from './nodes/message-node';
import { ActionNode } from './nodes/action-node';
import { ConditionNode } from './nodes/condition-node';
import { InputNode } from './nodes/input-node';
import { DelayNode } from './nodes/delay-node';
import { ReferralNode } from './nodes/referral-node';
import { NodePropertiesPanel } from './panels/node-properties-panel';

// ─── Props ──────────────────────────────────────────────────────

interface FlowEditorProps {
  scenario: ScenarioJson;
  positions?: Record<string, { x: number; y: number }> | null;
  onSave: (scenario: ScenarioJson) => void;
  onPositionsChange?: (positions: Record<string, { x: number; y: number }>) => void;
}

// ─── Node type registry ─────────────────────────────────────────

const nodeTypes: NodeTypes = {
  'scenario-message': MessageNode,
  'scenario-action': ActionNode,
  'scenario-condition': ConditionNode,
  'scenario-input': InputNode,
  'scenario-delay': DelayNode,
  'scenario-referral': ReferralNode,
};

// ─── Toolbar templates ──────────────────────────────────────────

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

// ─── MiniMap color mapping ──────────────────────────────────────

function miniMapNodeColor(node: Node): string {
  switch (node.type) {
    case 'scenario-message':
      return '#29b6f6';
    case 'scenario-action':
      return '#ab47bc';
    case 'scenario-condition':
      return '#ffa726';
    case 'scenario-input':
      return '#26a69a';
    case 'scenario-delay':
      return '#ff7043';
    case 'scenario-referral':
      return '#ec407a';
    default:
      return '#9e9e9e';
  }
}

// ─── Component ──────────────────────────────────────────────────

export function FlowEditor({
  scenario,
  positions: savedPositions,
  onSave: _onSave,
  onPositionsChange,
}: FlowEditorProps) {
  const [mounted, setMounted] = useState(false);
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);

  // Debounce position saves
  const positionTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Convert scenario -> flow data (memoized on scenario + positions)
  const initialFlow = useMemo(
    () => scenarioToFlow(scenario, savedPositions),
    // eslint-disable-next-line
    [scenario],
  );

  const [nodes, setNodes] = useState<Node[]>(initialFlow.nodes);
  const [edges, setEdges] = useState<Edge[]>(initialFlow.edges);

  // Re-sync when scenario prop changes
  useEffect(() => {
    const flow = scenarioToFlow(scenario, savedPositions);
    setNodes(flow.nodes);
    setEdges(flow.edges);
    // eslint-disable-next-line
  }, [scenario]);

  useEffect(() => {
    setMounted(true);
  }, []);

  // Node changes (drag, select, etc.)
  const onNodesChange: OnNodesChange = useCallback(
    (changes) => {
      setNodes((nds) => applyNodeChanges(changes, nds));

      // Debounce position persistence
      const hasDrag = changes.some((c) => c.type === 'position' && c.dragging === false);
      if (hasDrag && onPositionsChange) {
        if (positionTimerRef.current) clearTimeout(positionTimerRef.current);
        positionTimerRef.current = setTimeout(() => {
          setNodes((currentNodes) => {
            onPositionsChange(extractPositions(currentNodes));
            return currentNodes;
          });
        }, 500);
      }
    },
    [onPositionsChange],
  );

  // Edge changes (select, remove)
  const onEdgesChange: OnEdgesChange = useCallback((changes) => {
    setEdges((eds) => applyEdgeChanges(changes, eds));
  }, []);

  // Node click -> show properties panel
  const onNodeClick: NodeMouseHandler = useCallback((_event, node) => {
    setSelectedNodeId(node.id);
  }, []);

  // Pane click -> deselect
  const onPaneClick = useCallback(() => {
    setSelectedNodeId(null);
  }, []);

  // Properties panel: update node data
  const handleNodeUpdate = useCallback((nodeId: string, data: Record<string, unknown>) => {
    setNodes((nds) =>
      nds.map((n) => (n.id === nodeId ? { ...n, data: { ...n.data, ...data } } : n)),
    );
  }, []);

  // Properties panel: delete node
  const handleNodeDelete = useCallback((nodeId: string) => {
    setNodes((nds) => nds.filter((n) => n.id !== nodeId));
    setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId));
    setSelectedNodeId(null);
  }, []);

  const selectedNode = useMemo(
    () => nodes.find((n) => n.id === selectedNodeId) ?? null,
    [nodes, selectedNodeId],
  );

  const nodeCount = Object.keys(scenario.nodes).length;

  if (!mounted) {
    return (
      <div className="h-[700px] w-full rounded-xl border border-[var(--border)] flex items-center justify-center">
        <div className="h-8 w-8 animate-spin rounded-full border-4 border-[var(--primary)] border-t-transparent" />
      </div>
    );
  }

  return (
    <div className="w-full rounded-xl border border-[var(--border)] overflow-hidden relative">
      {/* Toolbar */}
      <div className="flex items-center justify-between border-b border-[var(--border)] bg-white px-3 py-2 z-10 relative">
        <div className="flex flex-wrap gap-1.5">
          {NODE_TEMPLATES.map((t) => (
            <button
              key={t.type}
              className="flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-[12px] font-medium text-[#424242] bg-[#fafafa] border border-[#e0e0e0] hover:bg-[#f5f5f5] hover:border-[#bdbdbd] transition-colors"
              title={`Добавить: ${t.label}`}
            >
              <span>{t.icon}</span> {t.label}
            </button>
          ))}
        </div>
        <div className="text-[11px] text-[#757575] ml-4 shrink-0">{nodeCount} нод</div>
      </div>

      {/* React Flow Canvas */}
      <div style={{ minHeight: 700, height: 700, width: '100%' }}>
        <ReactFlow
          nodes={nodes}
          edges={edges}
          nodeTypes={nodeTypes}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          onNodeClick={onNodeClick}
          onPaneClick={onPaneClick}
          snapToGrid
          snapGrid={[20, 20]}
          fitView
          fitViewOptions={{ padding: 0.15 }}
          minZoom={0.1}
          maxZoom={2}
          defaultEdgeOptions={{
            type: 'default',
            style: { strokeWidth: 2, stroke: '#29b6f6' },
            markerEnd: {
              type: MarkerType.ArrowClosed,
              width: 14,
              height: 14,
              color: '#29b6f6',
            },
          }}
          proOptions={{ hideAttribution: true }}
        >
          <Background variant={BackgroundVariant.Dots} gap={20} size={1} color="#e0e0e0" />
          <Controls showInteractive={false} position="bottom-left" />
          <MiniMap
            nodeColor={miniMapNodeColor}
            nodeStrokeWidth={2}
            pannable
            zoomable
            position="bottom-right"
            style={{ width: 160, height: 100 }}
          />
        </ReactFlow>
      </div>

      {/* Properties Panel */}
      {selectedNode && (
        <div onClick={(e) => e.stopPropagation()}>
          <NodePropertiesPanel
            node={selectedNode}
            onUpdate={handleNodeUpdate}
            onDelete={handleNodeDelete}
            onClose={() => setSelectedNodeId(null)}
          />
        </div>
      )}
    </div>
  );
}
