import React, { useMemo, useCallback } from 'react';
import {
  ReactFlow, Background, Controls, ReactFlowProvider,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';

import {
  NODE_IDS, NODE_KIND_MAP, SCORE_NODE_NAME_MAP,
  EDGES, SCORE_NODE_LABEL, INITIAL_NODE_POSITIONS,
} from './constants/canvasLayout';
import { useCanvasLayout } from '../../hooks/useCanvasLayout';

import ScoreNodeCard from './nodes/ScoreNodeCard';
import GateNodeCard  from './nodes/GateNodeCard';
import FixedNodeCard from './nodes/FixedNodeCard';
import CanvasToolbar from './CanvasToolbar';

const nodeTypes = {
  score: ScoreNodeCard,
  gate:  GateNodeCard,
  fixed: FixedNodeCard,
};

function buildEdges(weights) {
  return EDGES.map((e) => {
    const targetKind = NODE_KIND_MAP[e.target];
    const sourceKind = NODE_KIND_MAP[e.source];
    // gate → 점수: 해당 점수 노드 weight 가 활성인지에 따라 stroke
    let active = true;
    if (sourceKind === 'gate' && targetKind === 'score') {
      const nodeName = SCORE_NODE_NAME_MAP[e.target];
      active = (weights?.[nodeName] ?? 0) > 0;
    } else if (sourceKind === 'score' && targetKind === 'combine') {
      const nodeName = SCORE_NODE_NAME_MAP[e.source];
      active = (weights?.[nodeName] ?? 0) > 0;
    }
    return {
      ...e,
      animated: active,
      style: {
        stroke: active ? '#fbbf24' : '#374151',
        strokeWidth: active ? 1.5 : 1,
        strokeDasharray: active ? undefined : '4 4',
      },
    };
  });
}

function ScreenerCanvasInner({
  meta, settings, setLocal, result, running, dirty,
  onRunPreview, onRunSave, onPersistSettings,
}) {
  const { positions, updateNodePosition, reset } = useCanvasLayout(INITIAL_NODE_POSITIONS);

  const onWeightChange = useCallback((nodeId, weight) => {
    const name = SCORE_NODE_NAME_MAP[nodeId];
    if (!name) return;
    setLocal({ ...settings, weights: { ...settings.weights, [name]: weight } });
  }, [settings, setLocal]);

  const onParamsChange = useCallback((nodeId, params) => {
    const name = SCORE_NODE_NAME_MAP[nodeId];
    if (!name) return;
    setLocal({ ...settings, node_params: { ...settings.node_params, [name]: params } });
  }, [settings, setLocal]);

  const onGateParamsChange = useCallback((params) => {
    setLocal({ ...settings, gate_params: params });
  }, [settings, setLocal]);

  const scoreMetaByName = useMemo(() => {
    const map = {};
    for (const m of meta?.score_nodes ?? []) map[m.name] = m;
    return map;
  }, [meta]);

  const gateMeta = meta?.gate_nodes?.[0];

  const nodes = useMemo(() => {
    const arr = [];
    arr.push({
      id: NODE_IDS.DATA,
      type: 'fixed',
      position: positions[NODE_IDS.DATA],
      data: { icon: '📥', title: 'KRX 데이터', subtitle: '~2,800종목 · FDR', kind: 'data' },
      draggable: true,
    });
    arr.push({
      id: NODE_IDS.GATE,
      type: 'gate',
      position: positions[NODE_IDS.GATE],
      data: {
        meta: gateMeta,
        params: settings.gate_params,
        description: gateMeta?.label || '위생 게이트',
        onChange: onGateParamsChange,
      },
      draggable: true,
    });
    for (const [nodeId, backendName] of Object.entries(SCORE_NODE_NAME_MAP)) {
      const m = scoreMetaByName[backendName];
      const label = SCORE_NODE_LABEL[nodeId] || { icon: '📈', title: backendName };
      arr.push({
        id: nodeId,
        type: 'score',
        position: positions[nodeId],
        data: {
          meta: m ? { ...m, label: label.title } : { name: backendName, label: label.title },
          weight: settings.weights?.[backendName] ?? 0,
          params: settings.node_params?.[backendName] ?? {},
          summary: m?.summary || '',
          description: m?.description || m?.label || '',
          accent: m?.color || '#3b82f6',
          icon: label.icon,
          onWeightChange: (w) => onWeightChange(nodeId, w),
          onParamsChange: (p) => onParamsChange(nodeId, p),
        },
        draggable: true,
      });
    }
    const tp  = settings.top_n;
    const rr  = settings.rr_ratio;
    const am  = settings.atr_stop_mult;
    arr.push({
      id: NODE_IDS.COMBINE,
      type: 'fixed',
      position: positions[NODE_IDS.COMBINE],
      data: {
        icon: '⚙️',
        title: '가중합 + TopN + ATR',
        subtitle: `Top ${tp} · RR ${rr} · ATR×${am}`,
        kind: 'combine',
      },
      draggable: true,
    });
    const survivors = result?.survivors_count;
    const asof = result?.asof;
    arr.push({
      id: NODE_IDS.RESULT,
      type: 'fixed',
      position: positions[NODE_IDS.RESULT],
      data: {
        icon: '📊',
        title: '결과',
        subtitle: asof ? `${asof} · ${survivors ?? '-'}종목 통과` : '아직 실행 안 됨',
        kind: 'result',
      },
      draggable: true,
    });
    return arr;
  }, [positions, settings, meta, scoreMetaByName, gateMeta,
      onWeightChange, onParamsChange, onGateParamsChange, result]);

  const edges = useMemo(() => buildEdges(settings.weights), [settings.weights]);

  const handleNodeDragStop = useCallback((_evt, node) => {
    updateNodePosition(node.id, node.position);
  }, [updateNodePosition]);

  return (
    <div className="screener-canvas-wrap">
      <ReactFlow
        nodes={nodes}
        edges={edges}
        nodeTypes={nodeTypes}
        nodesConnectable={false}
        edgesUpdatable={false}
        edgesFocusable={false}
        onNodeDragStop={handleNodeDragStop}
        defaultViewport={{ x: 0, y: 0, zoom: 0.85 }}
        fitView
        fitViewOptions={{ padding: 0.2 }}
        proOptions={{ hideAttribution: true }}
      >
        <Background gap={20} size={1} color="#1f2937" />
        <Controls showInteractive={false} />
        <CanvasToolbar
          onRunPreview={onRunPreview}
          onRunSave={onRunSave}
          onPersistSettings={onPersistSettings}
          onResetLayout={reset}
          dirty={dirty}
          running={running}
        />
      </ReactFlow>
    </div>
  );
}

export default function ScreenerCanvas(props) {
  return (
    <ReactFlowProvider>
      <ScreenerCanvasInner {...props} />
    </ReactFlowProvider>
  );
}
