import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { JsonFormsDispatch, JsonFormsStateProvider } from "@jsonforms/react";
import Grid from "@mui/material/Grid2";
import { CollapseToggle } from "./CollapseToggle";
import {
  NodeContainerProps,
  NODE_SCHEMA,
  NODE_UISCHEMA,
  FOREIGNOBJECT_MAX_HEIGHT,
} from "./Interface";

export const NodeContainer = React.memo(function NodeContainer({
  nodeData,
  renderers,
  cells,
  enabled,
  nodeElements,
  nodeWidth,
  minHeight,
  hasChildren,
  isCollapsed,
  onToggle,
  onHeightChange,
  currentHeight,
  levelStyle,
  depth,
  isHighlighted,
  isNodeLoading,
  styleConfig,
}: NodeContainerProps) {
  const displayData = useMemo(() => {
    const { children, hasChildren: _hc, id, isHighlighted: _ih, ...rest } = nodeData;
    return rest;
  }, [nodeData]);
  const contentRef = useRef<HTMLDivElement>(null);
  const lastReportedH = useRef<number>(0);
  const [isNodeHovered, setIsNodeHovered] = useState(false);

  const initState = useMemo(() => ({
    core: {
      data: displayData,
      schema: NODE_SCHEMA,
      uischema: NODE_UISCHEMA,
    },
    renderers,
    cells,
  }), [displayData, renderers, cells]);

  const nodeHeight = Math.max(currentHeight, minHeight);
  const backgroundColor = levelStyle.backgroundColor ?? "#ffffff";
  const borderColor = levelStyle.borderColor ?? "#aaaaaa";
  const borderWidth = levelStyle.borderWidth ?? 1;
  const borderRadius = levelStyle.borderRadius ?? 6;
  const borderDash = levelStyle.borderDash ?? "0";
  const borderOpacity = levelStyle.borderOpacity ?? 1;

  useEffect(() => {
    const contentElement  = contentRef.current;
    if (!contentElement ) return;
    const updateMeasuredHeight = () => {
      const measured = Math.max(contentElement.offsetHeight, minHeight);
      if (measured !== lastReportedH.current) {
        lastReportedH.current = measured;
        onHeightChange(measured);
      }
    };
    const resizeObserver  = new ResizeObserver(updateMeasuredHeight);
    resizeObserver.observe(contentElement);
    const timer = setTimeout(updateMeasuredHeight, 80);
    return () => {
      resizeObserver.disconnect();
      clearTimeout(timer);
    };
  }, [minHeight, onHeightChange]);

  const getElement = useCallback(
    (uiSchemaElem: any, i: number) => (
      <Grid
        key={i}
        size={{
          xs: uiSchemaElem?.config?.layout?.xs ?? 12,
          sm: uiSchemaElem?.config?.layout?.sm ?? 12,
          md: uiSchemaElem?.config?.layout?.md ?? 12,
          lg: uiSchemaElem?.config?.layout?.lg ?? 12,
        }}
      >
        <JsonFormsDispatch
          schema={NODE_SCHEMA}
          uischema={uiSchemaElem}
          renderers={renderers}
          cells={cells}
          enabled={enabled}
        />
      </Grid>
    ),
    [renderers, cells, enabled]
  );

  return (
    <g
      onMouseEnter={() => setIsNodeHovered(true)}
      onMouseLeave={() => setIsNodeHovered(false)}
      onTouchStart={() => setIsNodeHovered(true)}
      onTouchEnd={() => setIsNodeHovered(false)}
      style={{
        transform: isHighlighted
          ? "translateY(-8px)"
          : isNodeHovered
          ? "translateY(-4px)"
          : "translateY(0px)",
        transition: "transform 0.2s ease",
      }}
    >
      {isHighlighted && (
        <rect
          x={-nodeWidth / 2 - 6}
          y={-nodeHeight / 2 - 6}
          width={nodeWidth + 12}
          height={nodeHeight + 12}
          rx={borderRadius + 6}
          fill="none"
          stroke={borderColor}
          strokeWidth={5}
          strokeOpacity={0.8}
        />
      )}

      <rect
        x={-nodeWidth / 2}
        y={-nodeHeight / 2}
        width={nodeWidth}
        height={nodeHeight}
        rx={borderRadius}
        fill={backgroundColor}
        stroke={borderColor}
        strokeWidth={borderWidth}
        strokeDasharray={borderDash}
        strokeOpacity={borderOpacity}
        filter={
          isNodeHovered || isHighlighted ? "url(#node-hover-shadow)" : undefined
        }
      />

      <foreignObject
        x={-nodeWidth / 2}
        y={-nodeHeight / 2}
        width={nodeWidth}
        height={FOREIGNOBJECT_MAX_HEIGHT}
        style={{ pointerEvents: "all" }}
        onMouseDown={(e) => e.stopPropagation()}
        onTouchStart={(e) => e.stopPropagation()}
        onTouchMove={(e) => e.stopPropagation()}
        onTouchEnd={(e) => e.stopPropagation()}
      >
        <div style={{ width: "100%", height: nodeHeight, overflow: "hidden" }}>
          <JsonFormsStateProvider initState={initState}>
            <div
              ref={contentRef}
              onClick={(e) => {
                if (!hasChildren) return;
                const interactive = (e.target as HTMLElement).closest(
                  'button, input, textarea, select, [role="button"], a'
                );
                if (!interactive) onToggle();
              }}
              style={{
                width: nodeWidth,
                boxSizing: "border-box",
                padding: "16px",
                cursor: hasChildren ? "pointer" : "default",
                position: "relative",
                ...styleConfig?.nodeContentStyle,
              }}
            >
              <Grid
                container
                rowSpacing={1}
                columnSpacing={1}
                spacing={1}
                sx={{ width: "100%", margin: 0 }}
              >
                {nodeElements.map((element: any, i: number) => (
                  <div key={i} style={{ display: "contents" }}>
                    {getElement(element, i)}
                  </div>
                ))}
              </Grid>
            </div>
          </JsonFormsStateProvider>
        </div>
      </foreignObject>

      {hasChildren && (
        <CollapseToggle
          isCollapsed={isCollapsed}
          borderColor={borderColor}
          cardHeight={nodeHeight}
          onToggle={onToggle}
          isLoading={isNodeLoading}
        />
      )}
    </g>
  );
});

