/**
 * ParallelCoordinates - Custom SVG implementation
 * Visualizes multi-dimensional optimization data with full control over rendering
 */

import { useMemo } from 'react';

interface Trial {
  trial_number: number;
  values: number[];
  params: Record<string, number>;
  design_variables?: Record<string, number>;
  user_attrs?: Record<string, any>;
  constraint_satisfied?: boolean;
  source?: 'FEA' | 'NN' | 'V10_FEA';
}

interface Objective {
  name: string;
  direction?: 'minimize' | 'maximize';
  unit?: string;
}

interface DesignVariable {
  name: string;
  unit?: string;
  min?: number;
  max?: number;
}

interface NivoParallelCoordinatesProps {
  trials: Trial[];
  objectives: Objective[];
  designVariables: DesignVariable[];
  paretoFront?: Trial[];
  height?: number;
}

// Colors optimized for dark theme
const COLORS = {
  background: '#0a0f1a',
  axis: '#3b82f6',
  axisLabel: '#94a3b8',
  tick: '#64748b',
  gridLine: '#1e3a5f',
  lineFEA: '#10b981',    // Green for FEA trials
  lineNN: '#f59e0b',     // Amber for NN trials
  linePareto: '#00d4e6', // Cyan for Pareto-optimal
  lineDefault: '#6366f1', // Indigo default
};

/**
 * Get parameter value from trial, checking multiple possible locations
 */
function getParamValue(trial: Trial, paramName: string): number | null {
  // Check params object first
  if (trial.params && typeof trial.params[paramName] === 'number') {
    return trial.params[paramName];
  }
  // Check design_variables (some backends use this)
  if (trial.design_variables && typeof trial.design_variables[paramName] === 'number') {
    return trial.design_variables[paramName];
  }
  // Try case-insensitive match
  if (trial.params) {
    const lowerName = paramName.toLowerCase();
    for (const [key, value] of Object.entries(trial.params)) {
      if (key.toLowerCase() === lowerName && typeof value === 'number') {
        return value;
      }
    }
  }
  return null;
}

/**
 * Get all available parameter names from trials
 */
function getAvailableParams(trials: Trial[]): string[] {
  const paramSet = new Set<string>();
  for (const trial of trials.slice(0, 10)) { // Sample first 10 trials
    if (trial.params) {
      Object.keys(trial.params).forEach(k => paramSet.add(k));
    }
    if (trial.design_variables) {
      Object.keys(trial.design_variables).forEach(k => paramSet.add(k));
    }
  }
  return Array.from(paramSet);
}

export function NivoParallelCoordinates({
  trials,
  objectives: _objectives,
  designVariables,
  paretoFront = [],
  height = 400
}: NivoParallelCoordinatesProps) {
  const width = 900;
  const margin = { top: 50, right: 60, bottom: 50, left: 60 };
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  // Create Pareto set for quick lookup
  const paretoSet = useMemo(() => {
    const set = new Set<number>();
    paretoFront.forEach(t => set.add(t.trial_number));
    return set;
  }, [paretoFront]);

  // Process data
  const { axes, lines, debugInfo } = useMemo(() => {
    // Get available params from actual trial data
    const availableParams = getAvailableParams(trials);

    // Determine which axes to show
    let axisNames: string[];
    if (designVariables.length > 0) {
      // Use design variables from config, but verify they exist in data
      axisNames = designVariables
        .slice(0, 6)
        .map(dv => dv.name)
        .filter(name => {
          // Check if at least some trials have this parameter
          return trials.slice(0, 20).some(t => getParamValue(t, name) !== null);
        });
    } else {
      // Fall back to params from trial data
      axisNames = availableParams.slice(0, 6);
    }

    if (!trials.length || axisNames.length < 2) {
      return {
        axes: [],
        lines: [],
        debugInfo: {
          trialsCount: trials.length,
          designVarsCount: designVariables.length,
          availableParams,
          axisNames,
          message: axisNames.length < 2
            ? 'Need at least 2 axes with valid data'
            : 'No trials available'
        }
      };
    }

    const axisSpacing = innerWidth / (axisNames.length - 1);

    // Calculate min/max for each axis from actual data
    const axesData = axisNames.map((name, i) => {
      const dv = designVariables.find(d => d.name === name);

      // Get all valid values from trials
      const values = trials
        .map(t => getParamValue(t, name))
        .filter((v): v is number => v !== null && isFinite(v));

      // Use config bounds if available, otherwise derive from data
      const dataMin = values.length ? Math.min(...values) : 0;
      const dataMax = values.length ? Math.max(...values) : 100;
      const range = dataMax - dataMin;

      // Add 5% padding to range if derived from data
      const min = dv?.min ?? (dataMin - range * 0.05);
      const max = dv?.max ?? (dataMax + range * 0.05);

      return {
        name,
        displayName: name.length > 12 ? name.substring(0, 10) + '...' : name,
        min,
        max,
        x: i * axisSpacing,
        unit: dv?.unit || '',
        valueCount: values.length,
      };
    });

    // Generate line paths for each trial
    const linesData = trials.slice(0, 100).map((trial, trialIndex) => {
      const points: { x: number; y: number }[] = [];

      axesData.forEach(axis => {
        const value = getParamValue(trial, axis.name);
        if (value !== null && isFinite(value)) {
          const range = axis.max - axis.min;
          const normalizedY = range > 0 ? (value - axis.min) / range : 0.5;
          const clampedY = Math.max(0, Math.min(1, normalizedY));
          const y = innerHeight - (clampedY * innerHeight); // Flip Y
          points.push({ x: axis.x, y });
        }
      });

      // Need at least 2 points to draw a line
      if (points.length >= 2) {
        const pathD = points
          .map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`)
          .join(' ');

        // Determine color based on trial type
        const isPareto = paretoSet.has(trial.trial_number);
        const source = trial.source || trial.user_attrs?.source;

        let color = COLORS.lineDefault;
        if (isPareto) {
          color = COLORS.linePareto;
        } else if (source === 'FEA' || source === 'V10_FEA') {
          color = COLORS.lineFEA;
        } else if (source === 'NN') {
          color = COLORS.lineNN;
        }

        return {
          id: `trial-${trial.trial_number ?? trialIndex}`,
          d: pathD,
          color,
          isPareto,
          opacity: isPareto ? 0.9 : 0.5,
          strokeWidth: isPareto ? 2 : 1,
        };
      }
      return null;
    }).filter((line): line is NonNullable<typeof line> => line !== null);

    return {
      axes: axesData,
      lines: linesData,
      debugInfo: {
        trialsCount: trials.length,
        designVarsCount: designVariables.length,
        availableParams,
        axisNames,
        linesGenerated: linesData.length,
        message: linesData.length > 0 ? 'Data processed successfully' : 'No valid line paths generated'
      }
    };
  }, [trials, designVariables, innerWidth, innerHeight, paretoSet]);

  // Show debug info if no valid visualization
  if (axes.length < 2) {
    return (
      <div className="p-4 bg-dark-800 rounded-lg">
        <div className="text-red-400 font-medium mb-2">
          Cannot render parallel coordinates
        </div>
        <div className="text-sm text-dark-400 space-y-1">
          <p>Trials: {debugInfo.trialsCount}</p>
          <p>Config design vars: {debugInfo.designVarsCount}</p>
          <p>Available params in data: {debugInfo.availableParams.join(', ') || 'none'}</p>
          <p>Matched axes: {debugInfo.axisNames?.join(', ') || 'none'}</p>
          <p className="text-yellow-400">{debugInfo.message}</p>
        </div>
      </div>
    );
  }

  return (
    <div className="w-full">
      {/* Debug info */}
      <div className="text-xs text-dark-500 mb-1 flex justify-between px-2">
        <span>{lines.length} of {trials.length} trials rendered</span>
        <span>{axes.length} axes</span>
      </div>

      <svg
        width={width}
        height={height}
        style={{ backgroundColor: COLORS.background }}
        className="rounded-lg mx-auto block"
        viewBox={`0 0 ${width} ${height}`}
      >
        <g transform={`translate(${margin.left}, ${margin.top})`}>
          {/* Draw grid lines */}
          {[0, 0.25, 0.5, 0.75, 1].map(pct => (
            <line
              key={`grid-${pct}`}
              x1={0}
              y1={innerHeight * (1 - pct)}
              x2={innerWidth}
              y2={innerHeight * (1 - pct)}
              stroke={COLORS.gridLine}
              strokeWidth={1}
              strokeDasharray="4,4"
              opacity={0.3}
            />
          ))}

          {/* Draw lines first (so axes appear on top) */}
          <g className="lines">
            {lines.map((line) => (
              <path
                key={line.id}
                d={line.d}
                fill="none"
                stroke={line.color}
                strokeWidth={line.strokeWidth}
                opacity={line.opacity}
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            ))}
          </g>

          {/* Draw axes */}
          {axes.map((axis) => (
            <g key={axis.name}>
              {/* Axis line */}
              <line
                x1={axis.x}
                y1={0}
                x2={axis.x}
                y2={innerHeight}
                stroke={COLORS.axis}
                strokeWidth={2}
              />

              {/* Axis label (top) */}
              <text
                x={axis.x}
                y={-25}
                textAnchor="middle"
                fill={COLORS.axisLabel}
                fontSize={11}
                fontWeight="500"
              >
                {axis.displayName}
              </text>

              {/* Max value label */}
              <text
                x={axis.x}
                y={-8}
                textAnchor="middle"
                fill={COLORS.tick}
                fontSize={9}
              >
                {axis.max.toFixed(axis.max > 100 ? 0 : 1)}
              </text>

              {/* Min value label */}
              <text
                x={axis.x}
                y={innerHeight + 15}
                textAnchor="middle"
                fill={COLORS.tick}
                fontSize={9}
              >
                {axis.min.toFixed(axis.min > 100 ? 0 : 1)}
              </text>

              {/* Unit label */}
              {axis.unit && (
                <text
                  x={axis.x}
                  y={innerHeight + 28}
                  textAnchor="middle"
                  fill={COLORS.tick}
                  fontSize={8}
                  opacity={0.7}
                >
                  ({axis.unit})
                </text>
              )}

              {/* Tick marks */}
              {[0, 0.5, 1].map(pct => (
                <line
                  key={`tick-${axis.name}-${pct}`}
                  x1={axis.x - 4}
                  y1={innerHeight * (1 - pct)}
                  x2={axis.x + 4}
                  y2={innerHeight * (1 - pct)}
                  stroke={COLORS.axis}
                  strokeWidth={1}
                />
              ))}
            </g>
          ))}
        </g>

        {/* Legend */}
        <g transform={`translate(${width - 120}, 15)`}>
          {paretoFront.length > 0 && (
            <g transform="translate(0, 0)">
              <line x1={0} y1={5} x2={20} y2={5} stroke={COLORS.linePareto} strokeWidth={2} />
              <text x={25} y={8} fill={COLORS.axisLabel} fontSize={9}>Pareto</text>
            </g>
          )}
          <g transform={`translate(0, ${paretoFront.length > 0 ? 15 : 0})`}>
            <line x1={0} y1={5} x2={20} y2={5} stroke={COLORS.lineFEA} strokeWidth={1} />
            <text x={25} y={8} fill={COLORS.axisLabel} fontSize={9}>FEA</text>
          </g>
          <g transform={`translate(0, ${paretoFront.length > 0 ? 30 : 15})`}>
            <line x1={0} y1={5} x2={20} y2={5} stroke={COLORS.lineNN} strokeWidth={1} />
            <text x={25} y={8} fill={COLORS.axisLabel} fontSize={9}>NN</text>
          </g>
        </g>
      </svg>
    </div>
  );
}
