import { useRef, useEffect, useState, useCallback } from 'react';
import { Maximize2, Minimize2 } from 'lucide-react';
import type { WaveformData } from '../../../lib/types';
import { formatAxisValue, TRACE_COLORS } from '../../../lib/waveform-utils';
import { cn } from '../../../lib/cn';
import { useFullscreen } from '../../../lib/useFullscreen';
import { ScopeWaveformViewer } from './ScopeWaveformViewer';

// uPlot is a vanilla JS lib; import it and its CSS
import uPlot from 'uplot';
import 'uplot/dist/uPlot.min.css';

interface WaveformViewerProps {
  waveform: WaveformData;
  className?: string;
}

function getWfColors() {
  const style = getComputedStyle(document.documentElement);
  return {
    axis: style.getPropertyValue('--color-wf-axis').trim() || '#475569',
    grid: style.getPropertyValue('--color-wf-grid').trim() || 'rgba(51, 65, 85, 0.5)',
    tick: style.getPropertyValue('--color-wf-tick').trim() || '#334155',
  };
}

function buildOpts(
  waveform: WaveformData,
  width: number,
  height: number,
  xType: string,
  yLabel: string,
): uPlot.Options {
  const traceNames = Object.keys(waveform.y_data);
  const wfColors = getWfColors();

  const series: uPlot.Series[] = [
    { label: xType === 'frequency' ? 'Frequency' : 'Time' },
    ...traceNames.map((name, i) => ({
      label: name,
      stroke: TRACE_COLORS[i % TRACE_COLORS.length],
      width: 2,
    })),
  ];

  const axes: uPlot.Axis[] = [
    {
      stroke: wfColors.axis,
      grid: { stroke: wfColors.grid, width: 1 },
      ticks: { stroke: wfColors.tick, width: 1 },
      font: '11px system-ui, sans-serif',
      values: (_u: uPlot, vals: number[]) =>
        vals.map((v) => formatAxisValue(v, xType)),
    },
    {
      stroke: wfColors.axis,
      grid: { stroke: wfColors.grid, width: 1 },
      ticks: { stroke: wfColors.tick, width: 1 },
      font: '11px system-ui, sans-serif',
      values: (_u: uPlot, vals: number[]) =>
        vals.map((v) => formatAxisValue(v, yLabel)),
    },
  ];

  return {
    width,
    height,
    series,
    axes,
    scales: {
      x: xType === 'frequency' ? { distr: 3 } : {},
    },
    cursor: {
      drag: { x: true, y: true, setScale: true },
    },
    legend: {
      show: true,
    },
  };
}

function TransientPlot({
  waveform,
  width,
  height,
}: {
  waveform: WaveformData;
  width: number;
  height?: number;
}) {
  const plotRef = useRef<HTMLDivElement>(null);
  const uplotRef = useRef<uPlot | null>(null);

  useEffect(() => {
    if (!plotRef.current || width <= 0) return;

    const traceNames = Object.keys(waveform.y_data);
    const xData = new Float64Array(waveform.x_data);
    const data: uPlot.AlignedData = [
      xData as unknown as number[],
      ...traceNames.map(
        (name) =>
          new Float64Array(waveform.y_data[name]) as unknown as number[],
      ),
    ];

    // Determine y-axis type from variable types
    const yType =
      waveform.variables.length > 1 ? waveform.variables[1].type : 'voltage';
    const opts = buildOpts(waveform, width, height ?? 280, waveform.x_type, yType);

    const u = new uPlot(opts, data, plotRef.current);
    uplotRef.current = u;

    return () => {
      u.destroy();
      uplotRef.current = null;
    };
  }, [waveform, width, height]);

  return <div ref={plotRef} />;
}

function ACPlot({
  waveform,
  width,
  height,
}: {
  waveform: WaveformData;
  width: number;
  height?: number;
}) {
  const magRef = useRef<HTMLDivElement>(null);
  const phaseRef = useRef<HTMLDivElement>(null);
  const magPlotRef = useRef<uPlot | null>(null);
  const phasePlotRef = useRef<uPlot | null>(null);

  useEffect(() => {
    if (!magRef.current || !phaseRef.current || width <= 0) return;

    const xData = new Float64Array(waveform.x_data);
    const magData = waveform.y_magnitude_db || {};
    const phaseData = waveform.y_phase_deg || {};
    const magTraces = Object.keys(magData);
    const phaseTraces = Object.keys(phaseData);

    // Magnitude plot
    if (magTraces.length > 0) {
      const magSeries: uPlot.AlignedData = [
        xData as unknown as number[],
        ...magTraces.map(
          (name) =>
            new Float64Array(magData[name]) as unknown as number[],
        ),
      ];

      const magH = height ? Math.round(height * 0.55) : 220;
      const magOpts = buildOpts(waveform, width, magH, 'frequency', 'dB');
      magOpts.series = [
        { label: 'Frequency' },
        ...magTraces.map((name, i) => ({
          label: `|${name}| (dB)`,
          stroke: TRACE_COLORS[i % TRACE_COLORS.length],
          width: 2,
        })),
      ];

      const u1 = new uPlot(magOpts, magSeries, magRef.current);
      magPlotRef.current = u1;
    }

    // Phase plot
    if (phaseTraces.length > 0) {
      const phaseSeries: uPlot.AlignedData = [
        xData as unknown as number[],
        ...phaseTraces.map(
          (name) =>
            new Float64Array(phaseData[name]) as unknown as number[],
        ),
      ];

      const phaseH = height ? height - Math.round(height * 0.55) : 180;
      const phaseOpts = buildOpts(
        waveform,
        width,
        phaseH,
        'frequency',
        'degrees',
      );
      phaseOpts.series = [
        { label: 'Frequency' },
        ...phaseTraces.map((name, i) => ({
          label: `Phase(${name})`,
          stroke: TRACE_COLORS[i % TRACE_COLORS.length],
          width: 2,
          dash: [6, 3],
        })),
      ];

      const u2 = new uPlot(phaseOpts, phaseSeries, phaseRef.current);
      phasePlotRef.current = u2;
    }

    return () => {
      magPlotRef.current?.destroy();
      phasePlotRef.current?.destroy();
      magPlotRef.current = null;
      phasePlotRef.current = null;
    };
  }, [waveform, width, height]);

  return (
    <div className="space-y-2">
      <div ref={magRef} />
      <div ref={phaseRef} />
    </div>
  );
}

/* Scope mode toggle icon — inline SVG of a simple oscilloscope shape */
function ScopeToggleIcon({ active }: { active: boolean }) {
  return (
    <svg
      width="18"
      height="18"
      viewBox="0 0 24 24"
      fill="none"
      stroke={active ? '#2dd4bf' : 'currentColor'}
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
    >
      {/* Monitor frame */}
      <rect x="2" y="3" width="20" height="14" rx="2" />
      {/* Sine wave trace */}
      <path d="M5 12 C7 8, 9 8, 11 12 S15 16, 17 12 L19 12" />
      {/* Stand */}
      <line x1="8" y1="21" x2="16" y2="21" />
      <line x1="12" y1="17" x2="12" y2="21" />
    </svg>
  );
}

export function WaveformViewer({ waveform, className }: WaveformViewerProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);
  const { isFullscreen, toggleFullscreen } = useFullscreen();
  const [scopeMode, setScopeMode] = useState(() => {
    try { return localStorage.getItem('spicebook-scope-mode') === 'true'; }
    catch { return false; }
  });

  const updateDimensions = useCallback(() => {
    if (containerRef.current) {
      setWidth(containerRef.current.clientWidth);
      setHeight(containerRef.current.clientHeight);
    }
  }, []);

  useEffect(() => {
    updateDimensions();
    const observer = new ResizeObserver(updateDimensions);
    if (containerRef.current) {
      observer.observe(containerRef.current);
    }
    return () => observer.disconnect();
  }, [updateDimensions]);

  const toggleScope = useCallback(() => {
    setScopeMode(prev => {
      const next = !prev;
      try { localStorage.setItem('spicebook-scope-mode', String(next)); } catch { /* noop */ }
      return next;
    });
  }, []);

  const traceCount = Object.keys(waveform.y_data).length;
  const isEmpty =
    traceCount === 0 && !waveform.y_magnitude_db && !waveform.y_phase_deg;

  // In fullscreen, subtract toolbar + padding from available height
  const plotWidth = isFullscreen ? Math.max(width - 32, 100) : width;
  const plotHeight = isFullscreen ? Math.max(height - 80, 200) : undefined;

  return (
    <div
      ref={containerRef}
      className={cn(
        'relative',
        className,
        isFullscreen && 'fixed inset-0 z-50 bg-sb-surface flex flex-col',
      )}
    >
      {/* Control buttons */}
      {!isEmpty && (
        <div
          className={cn(
            'flex items-center gap-1 z-10',
            isFullscreen
              ? 'px-4 py-2 justify-end border-b border-sb-border/50'
              : 'absolute top-1 right-1',
          )}
        >
          <button
            type="button"
            onClick={toggleFullscreen}
            className="p-1 rounded text-sb-muted hover:text-sb-text hover:bg-sb-border/50 transition-colors"
            title={isFullscreen ? 'Exit fullscreen (Esc)' : 'Fullscreen'}
          >
            {isFullscreen ? (
              <Minimize2 className="w-[18px] h-[18px]" />
            ) : (
              <Maximize2 className="w-[18px] h-[18px]" />
            )}
          </button>
          <button
            type="button"
            onClick={toggleScope}
            className="p-1 rounded hover:bg-sb-border/50 transition-colors"
            aria-label={scopeMode ? 'Exit oscilloscope view' : 'Switch to oscilloscope view'}
            title={scopeMode ? 'Standard view' : 'Oscilloscope view'}
          >
            <ScopeToggleIcon active={scopeMode} />
          </button>
        </div>
      )}

      {/* Plot area */}
      <div className={cn(isFullscreen && 'flex-1 min-h-0 px-4 pb-4')}>
        {isEmpty ? (
          <div className="text-sb-muted text-sm py-4 text-center">
            No waveform data to display.
          </div>
        ) : scopeMode ? (
          <ScopeWaveformViewer
            waveform={waveform}
            width={plotWidth}
            onExitScope={toggleScope}
          />
        ) : waveform.is_complex ? (
          <ACPlot waveform={waveform} width={plotWidth} height={plotHeight} />
        ) : (
          <TransientPlot waveform={waveform} width={plotWidth} height={plotHeight} />
        )}
      </div>
    </div>
  );
}
