import React, { useRef, useState, useEffect } from "react";
import { CreateWorkflowTemplateVersion } from "../services/WorkflowTemplateService";
import ValidationErrorIcon from "../../../../components/validationErrorIcon";

interface VisualiserTabProps {
  data: CreateWorkflowTemplateVersion;
  taskValidation: Record<string, boolean>;
}

interface ArrowPositions {
  levelIndex: number;
  positions: number[];
}

const VisualiserTab: React.FC<VisualiserTabProps> = ({
  data,
  taskValidation,
}) => {
  const tasks = data.tasks;
  const levels: CreateWorkflowTemplateVersion["tasks"][] = [];
  const levelRowRefs = useRef<(HTMLDivElement | null)[]>([]);
  const [arrowPositions, setArrowPositions] = useState<ArrowPositions[]>([]);
  const taskColumnMap = useRef<Map<string, number>>(new Map());
  const visualiserContainerRef = useRef<HTMLDivElement>(null);
  const [taskDOMPositions, setTaskDOMPositions] = useState<
    Map<string, { x: number; y: number }>
  >(new Map());

  // Calculate column range directly from taskColumnMap in render
  const allColumns = Array.from(taskColumnMap.current.values());
  const columnRange = {
    min: allColumns.length > 0 ? Math.min(...allColumns) : 0,
    max: allColumns.length > 0 ? Math.max(...allColumns) : 0,
  };

  if (tasks.length > 0) {
    const byGuid = new Map<string, CreateWorkflowTemplateVersion["tasks"][0]>();
    tasks.forEach((task) => {
      byGuid.set(task.config.guid as string, task);
    });

    const levelByGuid = new Map<string, number>();
    const columnByGuid = new Map<string, number>();

    const getLevel = (task: CreateWorkflowTemplateVersion["tasks"][0]) => {
      const guid = task.config.guid as string;
      const cached = levelByGuid.get(guid);
      if (cached !== undefined) {
        return cached;
      }

      const predecessors = task.config.predecessors as string[] | undefined;
      if (!predecessors || predecessors.length === 0) {
        levelByGuid.set(guid, 1);
        return 1;
      }

      const predecessorLevels = predecessors.map((pred) => {
        const predTask = byGuid.get(pred);
        if (!predTask) {
          return 1;
        }
        return getLevel(predTask);
      });

      const nextLevel = Math.max(...predecessorLevels, 0) + 1;
      levelByGuid.set(guid, nextLevel);
      return nextLevel;
    };

    // First pass: calculate levels for all tasks
    tasks.forEach((task) => {
      getLevel(task);
    });

    // Second pass: assign columns based on predecessor-child relationships
    const getColumn = (
      task: CreateWorkflowTemplateVersion["tasks"][0],
    ): number => {
      const guid = task.config.guid as string;
      const cached = columnByGuid.get(guid);
      if (cached !== undefined) {
        return cached;
      }

      const predecessors = task.config.predecessors as string[] | undefined;
      if (!predecessors || predecessors.length === 0) {
        columnByGuid.set(guid, 0);
        return 0;
      }

      // Get predecessor column and find which child index this is
      const predTask = byGuid.get(predecessors[0]);
      if (!predTask) {
        columnByGuid.set(guid, 0);
        return 0;
      }

      const predColumn = getColumn(predTask);

      // Find all siblings (tasks with same predecessor)
      const siblings = tasks.filter((t) => {
        const preds = t.config.predecessors as string[] | undefined;
        return preds && preds[0] === predecessors[0];
      });

      // Sort siblings by name for consistent ordering
      siblings.sort((a, b) => {
        const nameA = (a.config.name as string) || a.type || "";
        const nameB = (b.config.name as string) || b.type || "";
        return nameA.localeCompare(nameB);
      });

      const siblingIndex = siblings.findIndex(
        (s) => (s.config.guid as string) === guid,
      );

      // Assign columns: spread siblings around parent column
      const startCol = Math.max(
        0,
        predColumn - Math.floor(siblings.length / 2),
      );
      const column = startCol + siblingIndex;

      columnByGuid.set(guid, column);
      return column;
    };

    // Calculate columns for all tasks
    tasks.forEach((task) => {
      const col = getColumn(task);
      taskColumnMap.current.set(task.config.guid as string, col);
    });

    const levelsMap = new Map<number, CreateWorkflowTemplateVersion["tasks"]>();
    tasks.forEach((task) => {
      const level = getLevel(task);
      const list = levelsMap.get(level) ?? [];
      list.push(task);
      levelsMap.set(level, list);
    });

    // Sort tasks within each level by column
    [...levelsMap.entries()]
      .sort((a, b) => a[0] - b[0])
      .forEach((entry) => {
        entry[1].sort((a, b) => {
          const colA = taskColumnMap.current.get(a.config.guid as string) ?? 0;
          const colB = taskColumnMap.current.get(b.config.guid as string) ?? 0;
          return colA - colB;
        });
        levels.push(entry[1]);
      });
  }

  useEffect(() => {
    // Measure actual positions of task nodes and calculate arrow positions
    const positions: ArrowPositions[] = [];

    levelRowRefs.current.forEach((levelRow, levelIndex) => {
      if (!levelRow) return;

      const levelRect = levelRow.getBoundingClientRect();
      const nodes = levelRow.querySelectorAll(".visualiser-node");
      const nodePositions: number[] = [];

      nodes.forEach((node) => {
        const nodeRect = node.getBoundingClientRect();
        // Calculate center of node relative to level row
        const relativeCenter =
          (nodeRect.left - levelRect.left + nodeRect.width / 2) /
          levelRect.width;
        // Convert to SVG coordinates (0-100)
        nodePositions.push(relativeCenter * 100);
      });

      if (nodePositions.length > 0) {
        positions.push({ levelIndex, positions: nodePositions });
      }
    });

    setArrowPositions(positions);
  }, [tasks]);

  useEffect(() => {
    // Measure task DOM positions for outcome connectors
    if (!visualiserContainerRef.current) return;

    const containerRect =
      visualiserContainerRef.current.getBoundingClientRect();
    const positions = new Map<string, { x: number; y: number }>();

    const nodes =
      visualiserContainerRef.current.querySelectorAll(".visualiser-node");
    nodes.forEach((node) => {
      const taskName = node.querySelector(".visualiser-node-content span");
      if (!taskName) return;

      // Find task by name
      const task = tasks.find(
        (t) => (t.config.name as string) === taskName.textContent,
      );
      if (!task) return;

      const nodeRect = node.getBoundingClientRect();
      const relX =
        ((nodeRect.left - containerRect.left + nodeRect.width / 2) /
          containerRect.width) *
        100;
      const relY =
        ((nodeRect.top - containerRect.top + nodeRect.height / 2) /
          containerRect.height) *
        100;

      positions.set(task.config.guid as string, { x: relX, y: relY });
    });

    setTaskDOMPositions(positions);
  }, [tasks, levels]);

  const getArrowPositionsForLevel = (levelIndex: number): number[] => {
    const found = arrowPositions.find((ap) => ap.levelIndex === levelIndex);
    return found?.positions ?? [];
  };

  // Calculate connector positions based on grid columns
  const getConnectorPositionsByColumn = (
    levelTasks: CreateWorkflowTemplateVersion["tasks"][],
  ): number[] => {
    const numColumns = columnRange.max - columnRange.min + 1;
    if (numColumns <= 1) return [50];

    return levelTasks.map((task) => {
      const taskColumn =
        taskColumnMap.current.get(task.config.guid as string) ?? 0;
      const gridCol = taskColumn - columnRange.min;
      // Map grid column to percentage (e.g., 2 columns: col 0 = 25%, col 1 = 75%)
      return ((gridCol + 0.5) / numColumns) * 100;
    });
  };

  const renderConnector = (
    count: number,
    positions?: number[],
    predecessorPositions?: number[],
  ) => {
    if (count <= 1) {
      // For a single child, use predecessor position if available
      let startX = 50;
      let endX = 50;

      // If single child with predecessor positions, connect from the appropriate predecessor
      if (positions && positions.length === 1) {
        endX = positions[0];

        if (predecessorPositions && predecessorPositions.length > 0) {
          // Find the closest predecessor position to the child position
          const closest = predecessorPositions.reduce((prev, curr) =>
            Math.abs(curr - endX) < Math.abs(prev - endX) ? curr : prev,
          );
          startX = closest;
        }
      }

      // If start and end are the same, use simple vertical connector
      if (!predecessorPositions || predecessorPositions.length === 0) {
        return (
          <div aria-hidden="true" className="visualiser-connector">
            <div className="visualiser-connector-line" />
            <div className="visualiser-connector-arrow" />
            <div className="visualiser-connector-tail" />
          </div>
        );
      }

      // Otherwise render SVG with line from start to end (always, even if vertical)
      return (
        <svg
          aria-hidden="true"
          className="visualiser-connector-branch"
          viewBox="0 0 100 40"
          preserveAspectRatio="none"
        >
          <line
            className="visualiser-connector-branch-line"
            x1={startX}
            y1="0"
            x2={endX}
            y2="40"
          />
          <polygon
            className="visualiser-connector-branch-arrow"
            points={`${endX - 1.5},34 ${endX + 1.5},34 ${endX},40`}
          />
        </svg>
      );
    }

    // Use measured positions if available and valid, otherwise calculate evenly distributed
    const displayPositions =
      positions && positions.length > 0
        ? positions
        : Array.from(
            { length: count },
            (_, index) => ((index + 1) / (count + 1)) * 100,
          );

    // Guard against empty or invalid positions
    if (!displayPositions || displayPositions.length === 0) {
      return (
        <div aria-hidden="true" className="visualiser-connector">
          <div className="visualiser-connector-line" />
          <div className="visualiser-connector-arrow" />
          <div className="visualiser-connector-tail" />
        </div>
      );
    }

    const minX = Math.min(...displayPositions);
    const maxX = Math.max(...displayPositions);

    // Final safety check for Infinity
    if (!isFinite(minX) || !isFinite(maxX)) {
      return (
        <div aria-hidden="true" className="visualiser-connector">
          <div className="visualiser-connector-line" />
        </div>
      );
    }

    return (
      <svg
        aria-hidden="true"
        className="visualiser-connector-branch"
        viewBox="0 0 100 40"
        preserveAspectRatio="none"
      >
        <line
          className="visualiser-connector-branch-line"
          x1="50"
          y1="0"
          x2="50"
          y2="14"
        />
        <line
          className="visualiser-connector-branch-line"
          x1={minX}
          y1="14"
          x2={maxX}
          y2="14"
        />
        {displayPositions.map((x) => (
          <g key={x}>
            <line
              className="visualiser-connector-branch-line"
              x1={x}
              y1="14"
              x2={x}
              y2="28"
            />
            <polygon
              className="visualiser-connector-branch-arrow"
              points={`${x - 1.5},28 ${x + 1.5},28 ${x},34`}
            />
          </g>
        ))}
      </svg>
    );
  };

  // Collect all outcome-based connections
  const getOutcomeConnections = (): Array<{
    sourceGuid: string;
    targetGuid: string;
    verdict: string;
    sourceX: number;
    targetX: number;
    sourceY: number;
    targetY: number;
  }> => {
    const connections: Array<{
      sourceGuid: string;
      targetGuid: string;
      verdict: string;
      sourceX: number;
      targetX: number;
      sourceY: number;
      targetY: number;
    }> = [];

    tasks.forEach((task) => {
      const outcomes =
        (task.config.outcomeActions as Array<{
          verdict: string;
          task: string | null;
        }>) ?? [];

      outcomes.forEach((outcome) => {
        if (outcome.task) {
          const sourcePos = taskDOMPositions.get(task.config.guid as string);
          const targetPos = taskDOMPositions.get(outcome.task);

          if (sourcePos && targetPos) {
            connections.push({
              sourceGuid: task.config.guid as string,
              targetGuid: outcome.task,
              verdict: outcome.verdict,
              sourceX: sourcePos.x,
              sourceY: sourcePos.y,
              targetX: targetPos.x,
              targetY: targetPos.y,
            });
          }
        }
      });
    });

    return connections;
  };

  return (
    <div className="visualiser-root">
      <div className="visualiser-container" ref={visualiserContainerRef}>
        <div className="visualiser-flow">
          <div className="visualiser-node">
            <div className="visualiser-node-content">
              <span>Start</span>
            </div>
          </div>
          {levels.length > 0 &&
            renderConnector(
              levels[0].length,
              getConnectorPositionsByColumn(levels[0]),
              undefined,
            )}
          {levels.map((level, index) => (
            <React.Fragment key={`level-${index}`}>
              <div className="visualiser-level">
                <div
                  className="visualiser-level-row"
                  ref={(el) => {
                    levelRowRefs.current[index] = el;
                  }}
                  style={
                    {
                      gridTemplateColumns:
                        columnRange.max > columnRange.min
                          ? `repeat(${
                              columnRange.max - columnRange.min + 1
                            }, minmax(220px, 1fr))`
                          : "220px",
                    } as React.CSSProperties
                  }
                >
                  {level.map((task) => {
                    const taskColumn =
                      taskColumnMap.current.get(task.config.guid as string) ??
                      0;
                    const isRootTask = !(
                      task.config.predecessors as string[] | undefined
                    )?.length;
                    const numColumns = columnRange.max - columnRange.min + 1;

                    let styleObj: React.CSSProperties = {};

                    if (isRootTask && numColumns > 1) {
                      // Root tasks span all columns and center their content
                      styleObj = {
                        gridColumn: `1 / -1`,
                        display: "flex",
                        justifyContent: "center",
                      };
                    } else {
                      // Child tasks positioned in their specific column
                      const gridColumn = taskColumn - columnRange.min + 1;
                      styleObj = { gridColumn };
                    }

                    return (
                      <div
                        key={task.config.guid as string}
                        className="visualiser-node"
                        data-column={taskColumn}
                        style={styleObj}
                      >
                        <div className="visualiser-node-content">
                          <ValidationErrorIcon
                            visible={
                              taskValidation[task.config.guid as string] ===
                              false
                            }
                          />
                          <span>
                            {(task.config.name as string) || task.type}
                          </span>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
              {index < levels.length - 1
                ? renderConnector(
                    levels[index + 1].length,
                    getConnectorPositionsByColumn(levels[index + 1]),
                    getConnectorPositionsByColumn(levels[index]),
                  )
                : renderConnector(1, undefined, undefined)}
            </React.Fragment>
          ))}
          {levels.length === 0 && renderConnector(1)}
          <div className="visualiser-node">
            <div className="visualiser-node-content">
              <span>End</span>
            </div>
          </div>
        </div>
        {/* Outcome-based loop-back connectors overlay */}
        <svg
          aria-hidden="true"
          className="visualiser-outcome-connectors"
          viewBox="0 0 100 100"
          preserveAspectRatio="none"
        >
          {getOutcomeConnections().map((conn, idx) => {
            const controlOffsetX = 20;
            const controlOffsetY =
              (Math.abs(conn.targetY - conn.sourceY) / 2) * 0.3;

            // Calculate text position at the curve's midpoint (t=0.5 on Bezier curve)
            const t = 0.5;
            const p0 = { x: conn.sourceX, y: conn.sourceY };
            const p1 = {
              x: conn.sourceX - controlOffsetX,
              y: conn.sourceY + controlOffsetY,
            };
            const p2 = {
              x: conn.targetX - controlOffsetX,
              y: conn.targetY - controlOffsetY,
            };
            const p3 = { x: conn.targetX, y: conn.targetY };

            // Cubic Bezier curve formula at t
            const textX =
              Math.pow(1 - t, 3) * p0.x +
              3 * Math.pow(1 - t, 2) * t * p1.x +
              3 * (1 - t) * Math.pow(t, 2) * p2.x +
              Math.pow(t, 3) * p3.x;
            const textY =
              Math.pow(1 - t, 3) * p0.y +
              3 * Math.pow(1 - t, 2) * t * p1.y +
              3 * (1 - t) * Math.pow(t, 2) * p2.y +
              Math.pow(t, 3) * p3.y;

            return (
              <g
                key={`outcome-${idx}`}
                className="visualiser-outcome-connector"
              >
                <path
                  className="visualiser-outcome-connector-line"
                  d={`M ${conn.sourceX} ${conn.sourceY} 
                     C ${conn.sourceX - controlOffsetX} ${conn.sourceY + controlOffsetY}, 
                       ${conn.targetX - controlOffsetX} ${conn.targetY - controlOffsetY}, 
                       ${conn.targetX} ${conn.targetY}`}
                />
                <polygon
                  className="visualiser-outcome-connector-arrow"
                  points={`${conn.targetX - 1},${conn.targetY - 3} ${conn.targetX + 1},${conn.targetY - 3} ${conn.targetX},${conn.targetY}`}
                />
                <text
                  x={textX}
                  y={textY}
                  className="visualiser-outcome-connector-label"
                  textAnchor="middle"
                  dominantBaseline="middle"
                >
                  {conn.verdict}
                </text>
              </g>
            );
          })}
        </svg>
      </div>
    </div>
  );
};

export default VisualiserTab;
