/**
 * Pareto Front Plot - Protocol 13
 * Visualizes Pareto-optimal solutions for multi-objective optimization
 */

import { useState } from 'react';
import { ScatterChart, Scatter, Line, XAxis, YAxis, CartesianGrid, Tooltip, Cell, ResponsiveContainer, Legend } from 'recharts';

interface ParetoTrial {
  trial_number: number;
  values: [number, number];
  params: Record<string, number>;
  constraint_satisfied?: boolean;
}

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

interface ParetoPlotProps {
  paretoData: ParetoTrial[];
  objectives: Objective[];
  allTrials?: ParetoTrial[]; // All trials including non-Pareto
}

type NormalizationMode = 'raw' | 'minmax' | 'zscore';

export function ParetoPlot({ paretoData, objectives, allTrials }: ParetoPlotProps) {
  const [normMode, setNormMode] = useState<NormalizationMode>('raw');

  // Use allTrials if provided, otherwise fall back to paretoData
  const trialsToShow = allTrials && allTrials.length > 0 ? allTrials : paretoData;

  if (trialsToShow.length === 0) {
    return (
      <div className="bg-dark-700 rounded-lg p-6 border border-dark-600">
        <h3 className="text-lg font-semibold mb-4 text-dark-100">Pareto Front</h3>
        <div className="h-64 flex items-center justify-center text-dark-300">
          No Pareto front data yet
        </div>
      </div>
    );
  }

  // Create set of Pareto front trial numbers for easy lookup
  const paretoTrialNumbers = new Set(paretoData.map(t => t.trial_number));

  // Extract raw values for ALL trials
  const rawData = trialsToShow.map(trial => ({
    x: trial.values[0],
    y: trial.values[1],
    trial_number: trial.trial_number,
    feasible: trial.constraint_satisfied !== false,
    isPareto: paretoTrialNumbers.has(trial.trial_number)
  }));

  // Calculate statistics for normalization
  const xValues = rawData.map(d => d.x);
  const yValues = rawData.map(d => d.y);

  const xMin = Math.min(...xValues);
  const xMax = Math.max(...xValues);
  const yMin = Math.min(...yValues);
  const yMax = Math.max(...yValues);

  const xMean = xValues.reduce((a, b) => a + b, 0) / xValues.length;
  const yMean = yValues.reduce((a, b) => a + b, 0) / yValues.length;

  const xStd = Math.sqrt(xValues.reduce((sum, val) => sum + Math.pow(val - xMean, 2), 0) / xValues.length);
  const yStd = Math.sqrt(yValues.reduce((sum, val) => sum + Math.pow(val - yMean, 2), 0) / yValues.length);

  // Normalize data based on selected mode
  const normalizeX = (val: number): number => {
    if (normMode === 'minmax') {
      return xMax === xMin ? 0.5 : (val - xMin) / (xMax - xMin);
    } else if (normMode === 'zscore') {
      return xStd === 0 ? 0 : (val - xMean) / xStd;
    }
    return val; // raw
  };

  const normalizeY = (val: number): number => {
    if (normMode === 'minmax') {
      return yMax === yMin ? 0.5 : (val - yMin) / (yMax - yMin);
    } else if (normMode === 'zscore') {
      return yStd === 0 ? 0 : (val - yMean) / yStd;
    }
    return val; // raw
  };

  // Transform data with normalization
  const data = rawData.map(d => ({
    x: normalizeX(d.x),
    y: normalizeY(d.y),
    rawX: d.x,
    rawY: d.y,
    trial_number: d.trial_number,
    feasible: d.feasible,
    isPareto: d.isPareto
  }));

  // Sort ONLY Pareto front data by x-coordinate for line
  const paretoOnlyData = data.filter(d => d.isPareto).sort((a, b) => a.x - b.x);

  // Get objective labels with normalization indicator
  const normSuffix = normMode === 'minmax' ? ' [0-1]' : normMode === 'zscore' ? ' [z-score]' : '';
  const xLabel = objectives[0]
    ? `${objectives[0].name}${objectives[0].unit ? ` (${objectives[0].unit})` : ''}${normSuffix}`
    : `Objective 1${normSuffix}`;
  const yLabel = objectives[1]
    ? `${objectives[1].name}${objectives[1].unit ? ` (${objectives[1].unit})` : ''}${normSuffix}`
    : `Objective 2${normSuffix}`;

  // Custom tooltip (always shows raw values)
  const CustomTooltip = ({ active, payload }: any) => {
    if (!active || !payload || payload.length === 0) return null;

    const point = payload[0].payload;
    return (
      <div className="bg-dark-800 border border-dark-600 rounded p-3 shadow-lg">
        <div className="text-sm font-semibold text-dark-100 mb-2">
          Trial #{point.trial_number}
        </div>
        <div className="space-y-1 text-xs">
          <div className="text-dark-200">
            {objectives[0]?.name || 'Obj 1'}: <span className="font-mono">{point.rawX.toFixed(4)}</span>
          </div>
          <div className="text-dark-200">
            {objectives[1]?.name || 'Obj 2'}: <span className="font-mono">{point.rawY.toFixed(4)}</span>
          </div>
          <div className={point.feasible ? 'text-green-400' : 'text-red-400'}>
            {point.feasible ? '✓ Feasible' : '✗ Infeasible'}
          </div>
        </div>
      </div>
    );
  };

  return (
    <div className="bg-dark-700 rounded-lg p-6 border border-dark-600">
      <div className="flex items-center justify-between mb-4">
        <h3 className="text-lg font-semibold text-dark-100">
          Pareto Front ({paretoData.length} solutions)
        </h3>

        {/* Normalization Toggle */}
        <div className="flex gap-1 bg-dark-800 rounded p-1 border border-dark-600">
          <button
            onClick={() => setNormMode('raw')}
            className={`px-3 py-1 text-xs rounded transition-colors ${
              normMode === 'raw'
                ? 'bg-primary-500 text-white'
                : 'text-dark-300 hover:text-dark-100'
            }`}
          >
            Raw
          </button>
          <button
            onClick={() => setNormMode('minmax')}
            className={`px-3 py-1 text-xs rounded transition-colors ${
              normMode === 'minmax'
                ? 'bg-primary-500 text-white'
                : 'text-dark-300 hover:text-dark-100'
            }`}
          >
            Min-Max
          </button>
          <button
            onClick={() => setNormMode('zscore')}
            className={`px-3 py-1 text-xs rounded transition-colors ${
              normMode === 'zscore'
                ? 'bg-primary-500 text-white'
                : 'text-dark-300 hover:text-dark-100'
            }`}
          >
            Z-Score
          </button>
        </div>
      </div>
      <ResponsiveContainer width="100%" height={400}>
        <ScatterChart margin={{ top: 20, right: 20, bottom: 60, left: 60 }}>
          <CartesianGrid strokeDasharray="3 3" stroke="#334155" />
          <XAxis
            type="number"
            dataKey="x"
            name={objectives[0]?.name || 'Objective 1'}
            stroke="#94a3b8"
            label={{
              value: xLabel,
              position: 'insideBottom',
              offset: -45,
              fill: '#94a3b8',
              style: { fontSize: '14px' }
            }}
            tick={{ fill: '#94a3b8' }}
          />
          <YAxis
            type="number"
            dataKey="y"
            name={objectives[1]?.name || 'Objective 2'}
            stroke="#94a3b8"
            label={{
              value: yLabel,
              angle: -90,
              position: 'insideLeft',
              offset: -45,
              fill: '#94a3b8',
              style: { fontSize: '14px' }
            }}
            tick={{ fill: '#94a3b8' }}
          />
          <Tooltip content={<CustomTooltip />} />
          <Legend
            verticalAlign="top"
            height={36}
            content={() => (
              <div className="flex gap-4 justify-center mb-2">
                <div className="flex items-center gap-2">
                  <div className="w-3 h-3 rounded-full bg-green-400" />
                  <span className="text-sm text-dark-200">Feasible</span>
                </div>
                <div className="flex items-center gap-2">
                  <div className="w-3 h-3 rounded-full bg-red-400" />
                  <span className="text-sm text-dark-200">Infeasible</span>
                </div>
              </div>
            )}
          />
          {/* Pareto front line - only connects Pareto front points */}
          <Line
            type="monotone"
            data={paretoOnlyData}
            dataKey="y"
            stroke="#8b5cf6"
            strokeWidth={3}
            dot={false}
            connectNulls={false}
            isAnimationActive={false}
          />
          {/* All trials as scatter points */}
          <Scatter name="All Trials" data={data}>
            {data.map((entry, index) => (
              <Cell
                key={`cell-${index}`}
                fill={
                  entry.isPareto
                    ? (entry.feasible ? '#10b981' : '#ef4444')  // Pareto: green/red
                    : (entry.feasible ? '#64748b' : '#94a3b8')   // Non-Pareto: gray tones
                }
                r={entry.isPareto ? 7 : 4}
                opacity={entry.isPareto ? 1 : 0.4}
              />
            ))}
          </Scatter>
        </ScatterChart>
      </ResponsiveContainer>
    </div>
  );
}
