import React from 'react';
import type { DiagramNode as DiagramNodeType } from '../../api/queries/diagrams';
import type { NodeConfig, LatencyHeatmapEntry } from './types';
import type { NodeExecutionState } from '../ExecutionDiagram/types';
import { colorForType, iconForType, type IconElement } from './node-colors';
import { formatDurationShort } from '../../utils/format-utils';

const TOP_BAR_HEIGHT = 6;
const TEXT_LEFT = 32;
const TEXT_RIGHT_PAD = 24;
const CORNER_RADIUS = 4;

interface DiagramNodeProps {
  node: DiagramNodeType;
  isHovered: boolean;
  isSelected: boolean;
  config?: NodeConfig;
  executionState?: NodeExecutionState;
  overlayActive?: boolean;
  heatmapEntry?: LatencyHeatmapEntry;
  onClick: () => void;
  onDoubleClick?: () => void;
  onMouseEnter: () => void;
  onMouseLeave: () => void;
}

/** Interpolate green (120°) → yellow (60°) → red (0°) based on pctOfRoute */
function heatmapColor(pct: number): string {
  const clamped = Math.max(0, Math.min(100, pct));
  // 0% → hue 120 (green), 50% → hue 60 (yellow), 100% → hue 0 (red)
  const hue = 120 - (clamped / 100) * 120;
  return `hsl(${Math.round(hue)}, 70%, 92%)`;
}

function heatmapBorderColor(pct: number): string {
  const clamped = Math.max(0, Math.min(100, pct));
  const hue = 120 - (clamped / 100) * 120;
  return `hsl(${Math.round(hue)}, 60%, 50%)`;
}

export function DiagramNode({
  node, isHovered, isSelected, config,
  executionState, overlayActive, heatmapEntry,
  onClick, onDoubleClick, onMouseEnter, onMouseLeave,
}: DiagramNodeProps) {
  const x = node.x ?? 0;
  const y = node.y ?? 0;
  const w = node.width ?? 120;
  const h = node.height ?? 40;
  const color = colorForType(node.type);
  const iconElements = iconForType(node.type);

  // Extract label parts: type name and detail
  const typeName = node.type?.replace(/^EIP_/, '').replace(/_/g, ' ') ?? '';
  const detail = node.label || '';
  const resolvedUri = executionState?.resolvedEndpointUri;

  // Overlay state derivation
  const isCompleted = executionState?.status === 'COMPLETED';
  const isFailed = executionState?.status === 'FAILED';
  const isSkipped = overlayActive && !executionState;

  // Colors based on execution state (heatmap takes priority when no execution overlay)
  let cardFill = isHovered ? 'var(--bg-hover)' : 'var(--bg-surface)';
  let borderStroke = isHovered || isSelected ? color : 'var(--border-subtle)';
  let borderWidth = isHovered || isSelected ? 1.5 : 1;
  let topBarColor = color;
  let labelColor = 'var(--text-primary)';

  if (isCompleted) {
    cardFill = isHovered
      ? 'color-mix(in srgb, var(--success) 15%, var(--bg-surface))'
      : 'color-mix(in srgb, var(--success) 10%, var(--bg-surface))';
    borderStroke = 'var(--success)';
    borderWidth = 1.5;
    topBarColor = 'var(--success)';
  } else if (isFailed) {
    cardFill = isHovered
      ? 'color-mix(in srgb, var(--error) 15%, var(--bg-surface))'
      : 'color-mix(in srgb, var(--error) 10%, var(--bg-surface))';
    borderStroke = 'var(--error)';
    borderWidth = 2;
    topBarColor = 'var(--error)';
    labelColor = 'var(--error)';
  } else if (heatmapEntry && !overlayActive) {
    cardFill = heatmapColor(heatmapEntry.pctOfRoute);
    borderStroke = heatmapBorderColor(heatmapEntry.pctOfRoute);
    borderWidth = 1.5;
    topBarColor = heatmapBorderColor(heatmapEntry.pctOfRoute);
  }

  const statusColor = isCompleted ? 'var(--success)' : isFailed ? 'var(--error)' : undefined;

  return (
    <g
      data-node-id={node.id}
      transform={`translate(${x}, ${y})`}
      onClick={(e) => { e.stopPropagation(); onClick(); }}
      onDoubleClick={(e) => { e.stopPropagation(); onDoubleClick?.(); }}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
      style={{ cursor: 'pointer' }}
      opacity={isSkipped ? 0.35 : undefined}
    >
      {/* Selection ring */}
      {isSelected && (
        <rect
          x={-2}
          y={-2}
          width={w + 4}
          height={h + 4}
          rx={CORNER_RADIUS + 2}
          fill="none"
          stroke="var(--amber)"
          strokeWidth={2.5}
        />
      )}

      {/* Card background */}
      <rect
        x={0}
        y={0}
        width={w}
        height={h}
        rx={CORNER_RADIUS}
        fill={cardFill}
        stroke={borderStroke}
        strokeWidth={borderWidth}
      />

      {/* Colored top bar */}
      <rect x={0} y={0} width={w} height={TOP_BAR_HEIGHT} rx={CORNER_RADIUS} fill={topBarColor} />
      <rect x={CORNER_RADIUS} y={0} width={w - CORNER_RADIUS * 2} height={TOP_BAR_HEIGHT} fill={topBarColor} />

      {/* Clip path for text area */}
      <clipPath id={`clip-${node.id}`}>
        <rect x={TEXT_LEFT} y={TOP_BAR_HEIGHT} width={w - TEXT_LEFT - TEXT_RIGHT_PAD} height={h - TOP_BAR_HEIGHT} />
      </clipPath>

      {/* Icon (lucide 24×24 scaled to 14px) */}
      <g transform={`translate(6, ${h / 2 - 7}) scale(0.583)`}>
        {iconElements.map((el: IconElement, i: number) =>
          'd' in el
            ? <path key={i} d={el.d} fill="none" stroke={statusColor ?? color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
            : <circle key={i} cx={el.cx} cy={el.cy} r={el.r} fill="none" stroke={statusColor ?? color} strokeWidth={2} />
        )}
      </g>

      {/* Type name + detail + resolved URI (clipped to available width) */}
      <g clipPath={`url(#clip-${node.id})`}>
        {resolvedUri ? (
          <>
            <text x={TEXT_LEFT} y={TOP_BAR_HEIGHT + 12} fill={labelColor} fontSize={11} fontWeight={600}>
              {typeName}
            </text>
            {detail && detail !== typeName && (
              <text x={TEXT_LEFT} y={TOP_BAR_HEIGHT + 24} fill={isFailed ? 'var(--error)' : 'var(--text-secondary)'} fontSize={10}>
                {detail}
              </text>
            )}
            <text x={TEXT_LEFT} y={TOP_BAR_HEIGHT + (detail && detail !== typeName ? 35 : 24)} fill="var(--running)" fontSize={9} fontStyle="italic">
              → {resolvedUri.split('?')[0]}
            </text>
          </>
        ) : (
          <>
            <text x={TEXT_LEFT} y={h / 2 - 1} fill={labelColor} fontSize={11} fontWeight={600}>
              {typeName}
            </text>
            {detail && detail !== typeName && (
              <text x={TEXT_LEFT} y={h / 2 + 12} fill={isFailed ? 'var(--error)' : 'var(--text-secondary)'} fontSize={10}>
                {detail}
              </text>
            )}
          </>
        )}
      </g>

      {/* Inline badges row: hasTrace, hasTap, status — inside card, top-right */}
      {(() => {
        const BADGE_R = 6;
        const BADGE_D = BADGE_R * 2;
        const BADGE_GAP = 3;
        const cy = TOP_BAR_HEIGHT + BADGE_R + 2;
        const showTrace = config?.traceEnabled || executionState?.hasTraceData;
        const showTap = !!config?.tapExpression;
        if (!showTrace && !showTap && !isCompleted && !isFailed) return null;
        const badges: React.ReactNode[] = [];
        let slot = 0;

        // Status badge (rightmost, only during overlay)
        const statusCx = w - BADGE_R - 4;
        if (isCompleted) {
          badges.push(
            <g key="status">
              <circle cx={statusCx} cy={cy} r={BADGE_R} fill="var(--success)" />
              <path d={`M${statusCx - 3} ${cy} l2 2 4-4`} fill="none" stroke="var(--text-inverse)" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" />
            </g>
          );
          slot++;
        } else if (isFailed) {
          badges.push(
            <g key="status">
              <circle cx={statusCx} cy={cy} r={BADGE_R} fill="none" stroke="var(--error)" strokeWidth={2} opacity={0.5}>
                <animate attributeName="r" values="6;14" dur="1.5s" repeatCount="indefinite" />
                <animate attributeName="opacity" values="0.5;0" dur="1.5s" repeatCount="indefinite" />
              </circle>
              <circle cx={statusCx} cy={cy} r={BADGE_R} fill="none" stroke="var(--error)" strokeWidth={2} opacity={0.5}>
                <animate attributeName="r" values="6;14" dur="1.5s" begin="0.75s" repeatCount="indefinite" />
                <animate attributeName="opacity" values="0.5;0" dur="1.5s" begin="0.75s" repeatCount="indefinite" />
              </circle>
              <circle cx={statusCx} cy={cy} r={BADGE_R} fill="var(--error)" />
              <path d={`M${statusCx} ${cy - 3} v4 M${statusCx} ${cy + 2.5} v0.5`} fill="none" stroke="var(--text-inverse)" strokeWidth={1.5} strokeLinecap="round" />
            </g>
          );
          slot++;
        }

        // Tap badge (before status)
        if (showTap) {
          const tapCx = statusCx - slot * (BADGE_D + BADGE_GAP);
          badges.push(
            <g key="tap">
              <circle cx={tapCx} cy={cy} r={BADGE_R} fill="var(--purple)" />
              <g transform={`translate(${tapCx - 5}, ${cy - 5})`} stroke="var(--text-inverse)" strokeWidth={1.4} fill="none" strokeLinecap="round" strokeLinejoin="round">
                <path d="M5 1 C5 1 2 4.5 2 6.5a3 3 0 006 0C8 4.5 5 1 5 1z" />
              </g>
            </g>
          );
          slot++;
        }

        // Trace badge (leftmost)
        if (showTrace) {
          const traceCx = statusCx - slot * (BADGE_D + BADGE_GAP);
          const tracePulse = overlayActive && executionState?.hasTraceData;
          const traceHasData = executionState?.hasTraceData;
          badges.push(
            <g key="trace">
              {tracePulse && (
                <>
                  <circle cx={traceCx} cy={cy} r={BADGE_R} fill="none" stroke="var(--running)" strokeWidth={2} opacity={0.5}>
                    <animate attributeName="r" values={`${BADGE_R};${BADGE_R + 8}`} dur="1.5s" repeatCount="indefinite" />
                    <animate attributeName="opacity" values="0.5;0" dur="1.5s" repeatCount="indefinite" />
                  </circle>
                  <circle cx={traceCx} cy={cy} r={BADGE_R} fill="none" stroke="var(--running)" strokeWidth={2} opacity={0.5}>
                    <animate attributeName="r" values={`${BADGE_R};${BADGE_R + 8}`} dur="1.5s" begin="0.75s" repeatCount="indefinite" />
                    <animate attributeName="opacity" values="0.5;0" dur="1.5s" begin="0.75s" repeatCount="indefinite" />
                  </circle>
                </>
              )}
              <circle cx={traceCx} cy={cy} r={BADGE_R} fill="var(--running)" opacity={traceHasData ? 1 : 0.2} />
              <g transform={`translate(${traceCx - 5}, ${cy - 5}) scale(${10/24})`} stroke={traceHasData ? 'var(--text-inverse)' : 'var(--running)'} strokeWidth={2.4} fill="none" strokeLinecap="round" strokeLinejoin="round">
                <path d="M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0Z" />
                <path d="M20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0Z" />
                <path d="M16 17h4" />
                <path d="M4 13h4" />
              </g>
            </g>
          );
        }

        return <>{badges}</>;
      })()}

      {/* Execution overlay: duration text at bottom-right */}
      {executionState && statusColor && (
        <text
          x={w - 6}
          y={h - 4}
          textAnchor="end"
          fill={statusColor}
          fontSize={9}
          fontWeight={500}
        >
          {formatDurationShort(executionState.durationMs)}
        </text>
      )}

      {/* Heatmap: avg duration label at bottom-right */}
      {heatmapEntry && !overlayActive && !executionState && (
        <text
          x={w - 6}
          y={h - 4}
          textAnchor="end"
          fill={heatmapBorderColor(heatmapEntry.pctOfRoute)}
          fontSize={9}
          fontWeight={600}
        >
          {formatDurationShort(heatmapEntry.avgDurationMs)}
        </text>
      )}

      {/* Sub-route failure: drill-down arrow at bottom-left */}
      {isFailed && executionState?.subRouteFailed && (
        <g transform={`translate(4, ${h - 14})`}>
          <path
            d="M2 2 v5 a3 3 0 003 3 h5"
            fill="none" stroke="var(--error)" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round"
          />
          <path d="M8 8 l2 2 -2 2" fill="none" stroke="var(--error)" strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" />
        </g>
      )}
    </g>
  );
}
