/* 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';

type ReferralNodeType = {
  id: number;
  name: string;
  children: ReferralNodeType[];
};

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

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

function useDarkMode() {
  const [isDark, setIsDark] = useState(() =>
    document.documentElement.classList.contains('dark'),
  );

  useEffect(() => {
    const observer = new MutationObserver(() => {
      setIsDark(document.documentElement.classList.contains('dark'));
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class'],
    });
    return () => observer.disconnect();
  }, []);

  return isDark;
}

type Props = { data: ReferralNodeType };

const ReferralNode: React.FC<Props> = ({ data }) => {
  const { t } = useTranslation();
  const isDark = useDarkMode();
  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: ReferralNodeType): number =>
      1 + n.children.reduce((s, c) => s + count(c), 0);
    return count(data);
  }, [data]);

  const pathClass = isDark
    ? 'stroke-white/15 stroke-[1.5]'
    : 'stroke-black/10 stroke-[1.5]';

  return (
    <div className="flex h-[calc(100vh-22rem)] flex-col overflow-hidden">
      <div className="flex items-center justify-between border-b border-black/5 px-4 py-2">
        <div className="flex items-center gap-4 text-sm text-black/50 dark:text-white/50">
          <span>
            <span className="font-semibold text-black dark:text-white">
              {nodeCount}
            </span>{' '}
            {nodeCount === 1 ? t('member') : t('members')}
          </span>
          <span className="text-black/20 dark:text-white/20">|</span>
          <span>
            {t('Zoom')}:{' '}
            <span className="font-semibold text-black dark:text-white">
              {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 cursor-pointer items-center justify-center rounded-lg border border-black/10 bg-black/5 text-base font-medium text-black/60 hover:bg-black/10 dark:border-white/10 dark:bg-white/5 dark:text-white/60 dark:hover:bg-white/10"
            title={t('Zoom in')}
          >
            +
          </button>
          <button
            onClick={() => setZoom((z) => Math.max(z - 0.15, 0.2))}
            className="flex size-8 cursor-pointer items-center justify-center rounded-lg border border-black/10 bg-black/5 text-base font-medium text-black/60 hover:bg-black/10 dark:border-white/10 dark:bg-white/5 dark:text-white/60 dark:hover:bg-white/10"
            title={t('Zoom out')}
          >
            −
          </button>
          <button
            onClick={resetView}
            className="ml-1 h-8 cursor-pointer rounded-lg border border-black/10 bg-black/5 px-3 py-1 text-xs font-medium text-black/60 hover:bg-black/10 dark:border-white/10 dark:bg-white/5 dark:text-white/60 dark:hover:bg-white/10"
            title={t('Reset view')}
          >
            {t('Reset')}
          </button>
        </div>
      </div>

      <div className="relative flex-1" 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: 220, y: 120 }}
          translate={translate}
          renderCustomNodeElement={renderCardNode(t, isDark)}
          pathClassFunc={() => pathClass}
        />
      </div>
    </div>
  );
};

export default ReferralNode;

const renderCardNode =
  (t: (key: string) => string, isDark: boolean) =>
  ({ nodeDatum, toggleNode }: any) => {
    const width = 180;
    const height = 72;
    const level: number = nodeDatum.attributes?.level ?? 0;
    const isRoot = level === 0;

    const fillColor = isRoot
      ? '#81c522'
      : isDark
        ? 'rgba(255, 255, 255, 0.07)'
        : 'rgba(255, 255, 255, 0.80)';

    const strokeColor = isRoot
      ? 'transparent'
      : isDark
        ? 'rgba(255, 255, 255, 0.12)'
        : 'rgba(0, 0, 0, 0.08)';

    const textColor = isRoot ? '#0c1d26' : isDark ? '#ffffff' : '#0c1d26';

    const subTextColor = isRoot
      ? 'rgba(12,29,38,0.6)'
      : isDark
        ? 'rgba(255,255,255,0.45)'
        : 'rgba(12,29,38,0.45)';

    return (
      <g onClick={toggleNode} style={{ cursor: 'pointer' }}>
        <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 + 8}
              width={48}
              height={15}
              rx={7}
              fill="rgba(129, 197, 34, 0.15)"
            />
            <text
              x={-width / 2 + 34}
              y={-height / 2 + 19}
              textAnchor="middle"
              fontSize={9}
              fontWeight={700}
              fill="#81c522"
              strokeWidth={0}
            >
              {t('LEVEL')} {level}
            </text>
          </>
        )}

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

        {isRoot && (
          <text
            textAnchor="middle"
            fontSize={10}
            fontWeight={500}
            fill={subTextColor}
            y={16}
            strokeWidth={0}
          >
            {t('You')}
          </text>
        )}
      </g>
    );
  };
