/* eslint-disable @typescript-eslint/no-explicit-any */
import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import Tree from 'react-d3-tree';
import { useTranslation } from 'react-i18next';

export type ReferralNode = {
  id: number;
  name: string;
  email: string;
  referral_code?: string;
  children: ReferralNode[];
};

type TreeNode = {
  name: string;
  attributes: {
    email: string;
    level: number;
  };
  children?: TreeNode[];
};

function mapToTreeNode(node: ReferralNode, level = 0): TreeNode {
  return {
    name: node.name,
    attributes: {
      email: node.email ?? '',
      level,
    },
    children: node.children?.map((child) => mapToTreeNode(child, level + 1)),
  };
}

type Props = {
  data: ReferralNode;
};

const ReferralNode: React.FC<Props> = ({ data }) => {
  const { t } = useTranslation();
  const [translate, setTranslate] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(0.85);
  const containerRef = useRef<HTMLDivElement | null>(null);

  const resetView = useCallback(() => {
    if (containerRef.current) {
      const { width, height } = containerRef.current.getBoundingClientRect();
      setTranslate({ x: width / 2, y: height / 8 });
      setZoom(0.85);
    }
  }, []);

  useEffect(() => {
    resetView();
  }, [resetView]);

  const treeData = useMemo(() => mapToTreeNode(data), [data]);

  const nodeCount = useMemo(() => {
    const count = (n: ReferralNode): number =>
      1 + n.children.reduce((s, c) => s + count(c), 0);
    return count(data);
  }, [data]);

  return (
    <div className="flex h-[calc(100vh-12rem)] flex-col overflow-hidden rounded-2xl border border-black/5">
      <div className="flex shrink-0 items-center justify-between border-b border-black/5 bg-white px-4 py-2.5">
        <div className="flex items-center gap-4 text-sm text-black/50">
          <span>
            <span className="font-semibold text-black">{nodeCount}</span>{' '}
            {nodeCount === 1 ? t('member') : t('members')}
          </span>
          <span className="text-black/20">|</span>
          <span>
            {t('Zoom')}:{' '}
            <span className="font-semibold text-black">
              {Math.round(zoom * 100)}%
            </span>
          </span>
        </div>

        <div className="flex items-center gap-1">
          <button
            onClick={() => setZoom((z) => Math.min(z + 0.15, 2))}
            className="flex size-8 items-center justify-center rounded-lg border border-black/5 bg-black/2 text-base font-medium text-black/60 hover:bg-black/5"
            title={t('Zoom in')}
          >
            +
          </button>
          <button
            onClick={() => setZoom((z) => Math.max(z - 0.15, 0.2))}
            className="flex size-8 items-center justify-center rounded-lg border border-black/5 bg-black/2 text-base font-medium text-black/60 hover:bg-black/5"
            title={t('Zoom out')}
          >
            −
          </button>
          <button
            onClick={resetView}
            className="ml-1 rounded-lg border border-black/5 bg-black/2 px-3 py-1 text-xs font-medium text-black/60 hover:bg-black/5"
            title={t('Reset view')}
          >
            {t('Reset')}
          </button>
        </div>
      </div>

      <div
        className="relative flex-1 bg-[radial-gradient(circle,_rgba(0,0,0,0.05)_1px,_transparent_1px)] bg-[length:24px_24px]"
        ref={containerRef}
      >
        <Tree
          data={treeData}
          orientation="vertical"
          pathFunc="step"
          collapsible
          zoomable
          zoom={zoom}
          onUpdate={({ zoom: z }) => setZoom(z as number)}
          separation={{ siblings: 1.6, nonSiblings: 2.2 }}
          nodeSize={{ x: 240, y: 130 }}
          translate={translate}
          renderCustomNodeElement={renderCardNode(t)}
          pathClassFunc={() => 'stroke-black/10 stroke-[1.5]'}
        />
      </div>
    </div>
  );
};

export default ReferralNode;

const renderCardNode =
  (t: (key: string) => string) =>
  ({ nodeDatum, toggleNode }: any) => {
    const width = 200;
    const height = 80;
    const level: number = nodeDatum.attributes?.level ?? 0;

    const isRoot = level === 0;
    const fillColor = isRoot ? '#0F172A' : '#FFFFFF';
    const textColor = isRoot ? '#FFFFFF' : '#0F172A';
    const subTextColor = isRoot ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.45)';
    const strokeColor = isRoot ? 'transparent' : 'rgba(0,0,0,0.08)';

    return (
      <g onClick={toggleNode} style={{ cursor: 'pointer' }}>
        <rect
          x={-width / 2 + 2}
          y={-height / 2 + 2}
          width={width}
          height={height}
          rx={14}
          fill="rgba(0,0,0,0.06)"
        />

        <rect
          x={-width / 2}
          y={-height / 2}
          width={width}
          height={height}
          rx={14}
          fill={fillColor}
          stroke={strokeColor}
          strokeWidth={1}
        />

        {!isRoot && (
          <>
            <rect
              x={-width / 2 + 10}
              y={-height / 2 + 10}
              width={48}
              height={16}
              rx={8}
              fill="rgba(0,0,0,0.06)"
            />
            <text
              x={-width / 2 + 34}
              y={-height / 2 + 22}
              textAnchor="middle"
              fontSize={9}
              fontWeight={600}
              fill={subTextColor}
              strokeWidth={0}
            >
              {t('LEVEL')} {level}
            </text>
          </>
        )}

        <text
          textAnchor="middle"
          fontSize={13}
          fontWeight={600}
          fill={textColor}
          y={isRoot ? -8 : 4}
          strokeWidth={0}
        >
          {nodeDatum.name}
        </text>

        <text
          textAnchor="middle"
          fontSize={10}
          fontWeight={400}
          fill={subTextColor}
          y={isRoot ? 14 : 22}
          strokeWidth={0}
        >
          {nodeDatum.attributes?.email}
        </text>
      </g>
    );
  };
