/**
 * PlotlyConvergencePlot - Interactive convergence plot using Plotly
 *
 * Features:
 * - Line plot showing objective vs trial number
 * - Best-so-far trace overlay
 * - FEA vs NN trial differentiation
 * - Hover tooltips with trial details
 * - Range slider for zooming
 * - Export to PNG/SVG
 */

import { useMemo } from 'react';
import Plot from 'react-plotly.js';

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

// Penalty threshold - objectives above this are considered failed/penalty trials
const PENALTY_THRESHOLD = 100000;

interface PlotlyConvergencePlotProps {
  trials: Trial[];
  objectiveIndex?: number;
  objectiveName?: string;
  direction?: 'minimize' | 'maximize';
  height?: number;
  showRangeSlider?: boolean;
}

export function PlotlyConvergencePlot({
  trials,
  objectiveIndex = 0,
  objectiveName = 'Objective',
  direction = 'minimize',
  height = 400,
  showRangeSlider = true
}: PlotlyConvergencePlotProps) {

  // Process trials and calculate best-so-far
  const { feaData, nnData, bestSoFar, allX, allY } = useMemo(() => {
    if (!trials.length) return { feaData: { x: [], y: [], text: [] }, nnData: { x: [], y: [], text: [] }, bestSoFar: { x: [], y: [] }, allX: [], allY: [] };

    // Sort by trial number
    const sorted = [...trials].sort((a, b) => a.trial_number - b.trial_number);

    const fea: { x: number[]; y: number[]; text: string[] } = { x: [], y: [], text: [] };
    const nn: { x: number[]; y: number[]; text: string[] } = { x: [], y: [], text: [] };
    const best: { x: number[]; y: number[] } = { x: [], y: [] };
    const xs: number[] = [];
    const ys: number[] = [];

    let bestValue = direction === 'minimize' ? Infinity : -Infinity;

    sorted.forEach(t => {
      const val = t.values?.[objectiveIndex] ?? t.user_attrs?.[objectiveName] ?? null;
      if (val === null || !isFinite(val)) return;

      // Filter out failed/penalty trials:
      // 1. Objective above penalty threshold (e.g., 1000000 = solver failure)
      // 2. constraint_satisfied explicitly false
      // 3. user_attrs indicates pruned/failed
      const isPenalty = val >= PENALTY_THRESHOLD;
      const isFailed = t.constraint_satisfied === false;
      const isPruned = t.user_attrs?.pruned === true || t.user_attrs?.fail_reason;
      if (isPenalty || isFailed || isPruned) return;

      const source = t.source || t.user_attrs?.source || 'FEA';
      const hoverText = `Trial #${t.trial_number}<br>${objectiveName}: ${val.toFixed(4)}<br>Source: ${source}`;

      xs.push(t.trial_number);
      ys.push(val);

      if (source === 'NN') {
        nn.x.push(t.trial_number);
        nn.y.push(val);
        nn.text.push(hoverText);
      } else {
        fea.x.push(t.trial_number);
        fea.y.push(val);
        fea.text.push(hoverText);
      }

      // Update best-so-far
      if (direction === 'minimize') {
        if (val < bestValue) bestValue = val;
      } else {
        if (val > bestValue) bestValue = val;
      }
      best.x.push(t.trial_number);
      best.y.push(bestValue);
    });

    return { feaData: fea, nnData: nn, bestSoFar: best, allX: xs, allY: ys };
  }, [trials, objectiveIndex, objectiveName, direction]);

  if (!trials.length || allX.length === 0) {
    return (
      <div className="flex items-center justify-center h-64 text-gray-500">
        No trial data available
      </div>
    );
  }

  const traces: any[] = [];

  // FEA trials scatter
  if (feaData.x.length > 0) {
    traces.push({
      type: 'scatter',
      mode: 'markers',
      name: `FEA (${feaData.x.length})`,
      x: feaData.x,
      y: feaData.y,
      text: feaData.text,
      hoverinfo: 'text',
      marker: {
        color: '#3B82F6',
        size: 8,
        opacity: 0.7,
        line: { color: '#1E40AF', width: 1 }
      }
    });
  }

  // NN trials scatter
  if (nnData.x.length > 0) {
    traces.push({
      type: 'scatter',
      mode: 'markers',
      name: `NN (${nnData.x.length})`,
      x: nnData.x,
      y: nnData.y,
      text: nnData.text,
      hoverinfo: 'text',
      marker: {
        color: '#F97316',
        size: 6,
        symbol: 'cross',
        opacity: 0.6
      }
    });
  }

  // Best-so-far line
  if (bestSoFar.x.length > 0) {
    traces.push({
      type: 'scatter',
      mode: 'lines',
      name: 'Best So Far',
      x: bestSoFar.x,
      y: bestSoFar.y,
      line: {
        color: '#10B981',
        width: 3,
        shape: 'hv'  // Step line
      },
      hoverinfo: 'y'
    });
  }

  const layout: any = {
    height,
    margin: { l: 60, r: 30, t: 30, b: showRangeSlider ? 80 : 50 },
    paper_bgcolor: 'rgba(0,0,0,0)',
    plot_bgcolor: 'rgba(0,0,0,0)',
    xaxis: {
      title: 'Trial Number',
      gridcolor: '#E5E7EB',
      zerolinecolor: '#D1D5DB',
      rangeslider: showRangeSlider ? { visible: true } : undefined
    },
    yaxis: {
      title: objectiveName,
      gridcolor: '#E5E7EB',
      zerolinecolor: '#D1D5DB'
    },
    legend: {
      x: 1,
      y: 1,
      xanchor: 'right',
      bgcolor: 'rgba(255,255,255,0.8)',
      bordercolor: '#E5E7EB',
      borderwidth: 1
    },
    font: { family: 'Inter, system-ui, sans-serif' },
    hovermode: 'closest'
  };

  // Best value annotation
  const bestVal = direction === 'minimize'
    ? Math.min(...allY)
    : Math.max(...allY);
  const bestIdx = allY.indexOf(bestVal);
  const bestTrial = allX[bestIdx];

  return (
    <div className="w-full">
      {/* Summary stats */}
      <div className="flex gap-6 justify-center mb-3 text-sm">
        <div className="text-gray-600">
          Best: <span className="font-semibold text-green-600">{bestVal.toFixed(4)}</span>
          <span className="text-gray-400 ml-1">(Trial #{bestTrial})</span>
        </div>
        <div className="text-gray-600">
          Current: <span className="font-semibold">{allY[allY.length - 1].toFixed(4)}</span>
        </div>
        <div className="text-gray-600">
          Trials: <span className="font-semibold">{allX.length}</span>
        </div>
      </div>

      <Plot
        data={traces}
        layout={layout}
        config={{
          displayModeBar: true,
          displaylogo: false,
          modeBarButtonsToRemove: ['lasso2d', 'select2d'],
          toImageButtonOptions: {
            format: 'png',
            filename: 'convergence_plot',
            height: 600,
            width: 1200,
            scale: 2
          }
        }}
        style={{ width: '100%' }}
      />
    </div>
  );
}
