'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { ScenarioJson, ScenarioNodeJson } from './scenario-converter';
import {
  StartBlock,
  EndBlock,
  MessageBlock,
  ButtonBlock,
  ConditionBlock,
  ActionBlock,
  InputBlock,
  DelayBlock,
  ReferralBlock,
} from './tree-blocks';
import { VerticalArrow } from './tree-arrows';

const MAX_DEPTH = 15;

interface TreeRendererProps {
  scenario: ScenarioJson;
  onNodeClick?: (nodeId: string) => void;
}

export function TreeRenderer({ scenario, onNodeClick }: TreeRendererProps) {
  const nodes = scenario.nodes;
  const [zoom, setZoom] = useState(0.7);
  const containerRef = useRef<HTMLDivElement>(null);

  // Pan (drag) support
  const [isPanning, setIsPanning] = useState(false);
  const panStart = useRef({ x: 0, y: 0, scrollLeft: 0, scrollTop: 0 });

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const onMouseDown = (e: MouseEvent) => {
      // Only pan on middle button or when clicking empty space
      if (
        e.button === 1 ||
        (e.button === 0 && (e.target as HTMLElement).closest('.overflow-auto') === el)
      ) {
        setIsPanning(true);
        panStart.current = {
          x: e.clientX,
          y: e.clientY,
          scrollLeft: el.scrollLeft,
          scrollTop: el.scrollTop,
        };
        el.style.cursor = 'grabbing';
        e.preventDefault();
      }
    };
    const onMouseMove = (e: MouseEvent) => {
      if (!isPanning) return;
      el.scrollLeft = panStart.current.scrollLeft - (e.clientX - panStart.current.x);
      el.scrollTop = panStart.current.scrollTop - (e.clientY - panStart.current.y);
    };
    const onMouseUp = () => {
      setIsPanning(false);
      el.style.cursor = 'grab';
    };

    el.addEventListener('mousedown', onMouseDown);
    window.addEventListener('mousemove', onMouseMove);
    window.addEventListener('mouseup', onMouseUp);
    el.style.cursor = 'grab';

    return () => {
      el.removeEventListener('mousedown', onMouseDown);
      window.removeEventListener('mousemove', onMouseMove);
      window.removeEventListener('mouseup', onMouseUp);
    };
  }, [isPanning]);

  const handleNodeClick = useCallback((nodeId: string) => onNodeClick?.(nodeId), [onNodeClick]);

  // Pre-compute the render order: walk the tree, record which nodes expand.
  // This avoids React StrictMode double-render issues with mutable refs.
  const expandedNodes = useMemo(() => {
    const expanded = new Set<string>();
    function walk(nodeId: string, depth: number) {
      if (expanded.has(nodeId) || depth > MAX_DEPTH || !nodes[nodeId]) return;
      expanded.add(nodeId);
      const node = nodes[nodeId];

      // Walk button goto targets
      if (node.type === 'message') {
        const buttons = (node.data.buttons ?? []) as Array<{ action: string; target?: string }>;
        for (const btn of buttons) {
          if (btn.action === 'goto' && btn.target) walk(btn.target, depth + 1);
        }
      }
      // Walk condition targets
      if (node.type === 'condition') {
        const rules = (node.data.rules ?? []) as Array<{ target: string }>;
        for (const r of rules) if (r.target) walk(r.target, depth + 1);
        const def = node.data.default as string | undefined;
        if (def) walk(def, depth + 1);
      }
      // Walk referral targets
      if (node.type === 'referral') {
        const reach = node.data.onReach as string | undefined;
        const friend = node.data.friendStartNode as string | undefined;
        if (reach) walk(reach, depth + 1);
        if (friend) walk(friend, depth + 1);
      }
      // Walk next
      if (node.next) walk(node.next, depth + 1);
    }
    if (scenario.startNodeId) walk(scenario.startNodeId, 0);
    return expanded;
  }, [scenario.startNodeId, nodes]);

  // Center scroll on mount
  useEffect(() => {
    const el = containerRef.current;
    if (el) {
      requestAnimationFrame(() => {
        el.scrollLeft = (el.scrollWidth - el.clientWidth) / 2;
      });
    }
  }, [expandedNodes]);

  if (!scenario.startNodeId || !nodes[scenario.startNodeId]) {
    return (
      <div
        className="flex items-center justify-center rounded-xl border-2 border-dashed border-gray-300"
        style={{ minHeight: 300 }}
      >
        <p className="text-sm text-gray-400">Сценарий пуст</p>
      </div>
    );
  }

  return (
    <div className="relative">
      {/* Zoom controls */}
      <div className="sticky top-0 right-0 z-10 flex items-center gap-1 p-2 justify-end">
        <button
          onClick={() => setZoom((z) => Math.max(0.3, z - 0.1))}
          className="w-7 h-7 rounded bg-white border text-sm shadow-sm hover:bg-gray-50"
        >
          −
        </button>
        <span className="text-[10px] text-gray-500 w-10 text-center">
          {Math.round(zoom * 100)}%
        </span>
        <button
          onClick={() => setZoom((z) => Math.min(1.5, z + 0.1))}
          className="w-7 h-7 rounded bg-white border text-sm shadow-sm hover:bg-gray-50"
        >
          +
        </button>
        <button
          onClick={() => setZoom(0.85)}
          className="w-7 h-7 rounded bg-white border text-[9px] shadow-sm hover:bg-gray-50"
        >
          ⊙
        </button>
      </div>

      <div
        ref={containerRef}
        className="overflow-auto"
        style={{
          maxHeight: '75vh',
          minHeight: 400,
          backgroundColor: '#fafafa',
          userSelect: isPanning ? 'none' : 'auto',
        }}
      >
        <div
          style={{
            transform: `scale(${zoom})`,
            transformOrigin: 'top center',
            padding: '24px 40px 60px',
            minWidth: 'fit-content',
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
          }}
        >
          <StartBlock onClick={() => handleNodeClick('__start__')} />
          <VerticalArrow />

          <SequenceRenderer
            nodeId={scenario.startNodeId}
            nodes={nodes}
            expandedNodes={expandedNodes}
            renderVisited={new Set()}
            depth={0}
            onNodeClick={handleNodeClick}
          />

          <VerticalArrow />
          <EndBlock onClick={() => handleNodeClick('__end__')} />
        </div>
      </div>
    </div>
  );
}

// ─── Ref stub (already visited node) ────────────────────────────

function RefStub({ nodeId, nodeType }: { nodeId: string; nodeType?: string }) {
  return (
    <div
      className="rounded-lg text-center cursor-default"
      style={{
        padding: '8px 14px',
        border: '2px dashed #90caf9',
        backgroundColor: '#e3f2fd',
        fontSize: 10,
        color: '#1565c0',
        maxWidth: 150,
      }}
    >
      ↗ {nodeType ? `${nodeType}: ` : ''}
      {nodeId.length > 20 ? nodeId.slice(0, 20) + '…' : nodeId}
    </div>
  );
}

// ─── SequenceRenderer ───────────────────────────────────────────

interface SeqProps {
  nodeId: string;
  nodes: Record<string, ScenarioNodeJson>;
  expandedNodes: Set<string>;
  renderVisited: Set<string>;
  depth: number;
  onNodeClick: (nodeId: string) => void;
}

function SequenceRenderer({
  nodeId,
  nodes,
  expandedNodes,
  renderVisited,
  depth,
  onNodeClick,
}: SeqProps) {
  if (depth > MAX_DEPTH) return <RefStub nodeId={nodeId} />;

  // Already rendered in this render pass → show stub
  if (renderVisited.has(nodeId)) {
    return <RefStub nodeId={nodeId} nodeType={nodes[nodeId]?.type} />;
  }

  const node = nodes[nodeId];
  if (!node) {
    return (
      <div
        className="rounded-lg"
        style={{
          padding: '6px 12px',
          border: '2px dashed #bdbdbd',
          backgroundColor: '#f5f5f5',
          fontSize: 10,
          color: '#9e9e9e',
          textAlign: 'center',
        }}
      >
        ? {nodeId}
      </div>
    );
  }

  // Create a new visited set with this node added (immutable approach for StrictMode)
  const newVisited = new Set(renderVisited);
  newVisited.add(nodeId);

  const common = { nodes, expandedNodes, renderVisited: newVisited, depth, onNodeClick };

  return (
    <div className="flex flex-col items-center">
      <NodeBlock node={node} onNodeClick={onNodeClick} />

      {node.type === 'message' && <MessageBranches node={node} {...common} />}
      {node.type === 'condition' && <ConditionBranches node={node} {...common} />}
      {node.type === 'referral' && <ReferralBranches node={node} {...common} />}

      {node.next && nodes[node.next] && (
        <>
          <VerticalArrow />
          <SequenceRenderer nodeId={node.next} {...common} depth={depth + 1} />
        </>
      )}
      {node.next && !nodes[node.next] && (
        <>
          <VerticalArrow />
          <RefStub nodeId={node.next} />
        </>
      )}
    </div>
  );
}

// ─── Message buttons + fan-out ──────────────────────────────────

function MessageBranches({
  node,
  nodes,
  expandedNodes,
  renderVisited,
  depth,
  onNodeClick,
}: {
  node: ScenarioNodeJson;
  nodes: Record<string, ScenarioNodeJson>;
  expandedNodes: Set<string>;
  renderVisited: Set<string>;
  depth: number;
  onNodeClick: (id: string) => void;
}) {
  const buttons = (node.data.buttons ?? []) as Array<{
    text: string;
    action: string;
    target?: string;
    url?: string;
  }>;
  if (buttons.length === 0) return null;

  // Collect goto targets that can be expanded
  const gotoButtons = buttons
    .filter((b) => b.action === 'goto' && b.target && nodes[b.target])
    .map((b) => ({ text: b.text, targetId: b.target! }));

  // Buttons that can branch (target not yet visited)
  const expandable = gotoButtons.filter((b) => !renderVisited.has(b.targetId));

  // Bitrix-style: integrated columns — each column has its line segment + arrow + branch content
  return (
    <>
      <VerticalArrow />
      {/* Buttons row */}
      <div className="flex gap-2 justify-center flex-wrap">
        {buttons.map((btn, i) => (
          <ButtonBlock
            key={i}
            text={btn.text}
            action={btn.action}
            target={btn.target}
            url={btn.url}
            onClick={() => onNodeClick(node.id)}
          />
        ))}
      </div>

      {/* Integrated fan-out + branches in one flex container */}
      {expandable.length > 0 && (
        <div style={{ width: '100%' }}>
          {/* Vertical line from buttons to horizontal bar */}
          <div className="flex justify-center">
            <div style={{ width: 2, height: 16, backgroundColor: '#C0C1C3' }} />
          </div>

          {/* Columns: horizontal line + vertical drop + branch */}
          <div className="flex">
            {expandable.map((b, i) => (
              <div key={i} className="flex-1 flex flex-col items-center" style={{ minWidth: 150 }}>
                {/* Horizontal line segment */}
                <div
                  style={{
                    height: 2,
                    backgroundColor: '#C0C1C3',
                    width:
                      expandable.length === 1
                        ? 0
                        : i === 0
                          ? '50%'
                          : i === expandable.length - 1
                            ? '50%'
                            : '100%',
                    alignSelf:
                      i === 0 ? 'flex-end' : i === expandable.length - 1 ? 'flex-start' : 'stretch',
                  }}
                />
                {/* Vertical drop + arrow */}
                <div style={{ width: 2, height: 14, backgroundColor: '#C0C1C3' }} />
                <div
                  style={{
                    width: 0,
                    height: 0,
                    borderLeft: '4px solid transparent',
                    borderRight: '4px solid transparent',
                    borderTop: '5px solid #C0C1C3',
                  }}
                />
                {/* Branch content */}
                <div className="mt-1">
                  <SequenceRenderer
                    nodeId={b.targetId}
                    nodes={nodes}
                    expandedNodes={expandedNodes}
                    renderVisited={renderVisited}
                    depth={depth + 1}
                    onNodeClick={onNodeClick}
                  />
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Stubs for already-visited targets */}
      {gotoButtons.filter((b) => renderVisited.has(b.targetId)).length > 0 &&
        expandable.length === 0 && (
          <>
            <VerticalArrow />
            <div className="flex gap-2 justify-center flex-wrap">
              {gotoButtons
                .filter((b) => renderVisited.has(b.targetId))
                .map((b, i) => (
                  <RefStub key={i} nodeId={b.targetId} nodeType={nodes[b.targetId]?.type} />
                ))}
            </div>
          </>
        )}
    </>
  );
}

// ─── Condition branches ─────────────────────────────────────────

function ConditionBranches({
  node,
  nodes,
  expandedNodes,
  renderVisited,
  depth,
  onNodeClick,
}: {
  node: ScenarioNodeJson;
  nodes: Record<string, ScenarioNodeJson>;
  expandedNodes: Set<string>;
  renderVisited: Set<string>;
  depth: number;
  onNodeClick: (id: string) => void;
}) {
  const rules = (node.data.rules ?? []) as Array<{ condition: string; target: string }>;
  const def = node.data.default as string | undefined;

  const branches: Array<{ label: string; targetId: string }> = [];
  for (const rule of rules) {
    if (rule.target && nodes[rule.target]) {
      branches.push({ label: rule.condition, targetId: rule.target });
    }
  }
  if (def && nodes[def]) {
    branches.push({ label: 'иначе', targetId: def });
  }

  if (branches.length === 0) return null;

  return (
    <div style={{ width: '100%' }}>
      <div className="flex justify-center">
        <div style={{ width: 2, height: 16, backgroundColor: '#C0C1C3' }} />
      </div>
      <div className="flex">
        {branches.map((b, i) => (
          <div key={i} className="flex-1 flex flex-col items-center" style={{ minWidth: 150 }}>
            <div
              style={{
                height: 2,
                backgroundColor: '#C0C1C3',
                width:
                  branches.length === 1
                    ? 0
                    : i === 0
                      ? '50%'
                      : i === branches.length - 1
                        ? '50%'
                        : '100%',
                alignSelf:
                  i === 0 ? 'flex-end' : i === branches.length - 1 ? 'flex-start' : 'stretch',
              }}
            />
            <div style={{ width: 2, height: 14, backgroundColor: '#C0C1C3' }} />
            <div
              style={{
                width: 0,
                height: 0,
                borderLeft: '4px solid transparent',
                borderRight: '4px solid transparent',
                borderTop: '5px solid #C0C1C3',
              }}
            />
            <div className="text-[9px] text-[#ff9800] font-semibold mb-1 mt-1 text-center">
              {b.label}
            </div>
            <SequenceRenderer
              nodeId={b.targetId}
              nodes={nodes}
              expandedNodes={expandedNodes}
              renderVisited={renderVisited}
              depth={depth + 1}
              onNodeClick={onNodeClick}
            />
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── Referral branches ──────────────────────────────────────────

function ReferralBranches({
  node,
  nodes,
  expandedNodes,
  renderVisited,
  depth,
  onNodeClick,
}: {
  node: ScenarioNodeJson;
  nodes: Record<string, ScenarioNodeJson>;
  expandedNodes: Set<string>;
  renderVisited: Set<string>;
  depth: number;
  onNodeClick: (id: string) => void;
}) {
  const reach = node.data.onReach as string | undefined;
  const friend = node.data.friendStartNode as string | undefined;
  const branches: Array<{ label: string; targetId: string }> = [];
  if (reach && nodes[reach]) branches.push({ label: '🏆 Цель', targetId: reach });
  if (friend && nodes[friend]) branches.push({ label: '👋 Друг', targetId: friend });

  if (branches.length === 0) return null;

  return (
    <div style={{ width: '100%' }}>
      <div className="flex justify-center">
        <div style={{ width: 2, height: 16, backgroundColor: '#C0C1C3' }} />
      </div>
      <div className="flex">
        {branches.map((b, i) => (
          <div key={i} className="flex-1 flex flex-col items-center" style={{ minWidth: 150 }}>
            <div
              style={{
                height: 2,
                backgroundColor: '#C0C1C3',
                width:
                  branches.length === 1
                    ? 0
                    : i === 0
                      ? '50%'
                      : i === branches.length - 1
                        ? '50%'
                        : '100%',
                alignSelf:
                  i === 0 ? 'flex-end' : i === branches.length - 1 ? 'flex-start' : 'stretch',
              }}
            />
            <div style={{ width: 2, height: 14, backgroundColor: '#C0C1C3' }} />
            <div
              style={{
                width: 0,
                height: 0,
                borderLeft: '4px solid transparent',
                borderRight: '4px solid transparent',
                borderTop: '5px solid #C0C1C3',
              }}
            />
            <div className="text-[9px] text-[#ec407a] font-semibold mb-1 mt-1 text-center">
              {b.label}
            </div>
            <SequenceRenderer
              nodeId={b.targetId}
              nodes={nodes}
              expandedNodes={expandedNodes}
              renderVisited={renderVisited}
              depth={depth + 1}
              onNodeClick={onNodeClick}
            />
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── NodeBlock dispatcher ───────────────────────────────────────

function NodeBlock({
  node,
  onNodeClick,
}: {
  node: ScenarioNodeJson;
  onNodeClick: (id: string) => void;
}) {
  const click = () => onNodeClick(node.id);

  switch (node.type) {
    case 'message':
      return <MessageBlock node={node} onClick={click} />;
    case 'condition':
      return <ConditionBlock node={node} onClick={click} />;
    case 'action':
      return <ActionBlock node={node} onClick={click} />;
    case 'input':
      return <InputBlock node={node} onClick={click} />;
    case 'delay':
      return <DelayBlock node={node} onClick={click} />;
    case 'referral':
      return <ReferralBlock node={node} onClick={click} />;
    default:
      return (
        <div
          onClick={click}
          className="rounded-lg border border-gray-300 bg-white px-3 py-2 text-xs text-gray-600 cursor-pointer"
        >
          {node.type}: {node.id}
        </div>
      );
  }
}
