/** Single-variable trajectory plot: observed (cyan) overlaid with predicted
 * (coral, optionally clipped to a `predictedProgress` fraction). */

import { useEffect, useRef } from "react";

import type { TrajectorySample } from "@/types/physix";
import {
  DEFAULT_PADDING,
  buildProjection,
  computeBounds,
  drawAxes,
  drawSeries,
} from "@/lib/trajectory";

const OBSERVED_COLOR = "#5cc8f9";
const PREDICTED_COLOR = "#f97a5c";
const LEGEND_OBSERVED_LABEL = "Observed";
const LEGEND_PREDICTED_LABEL = "Predicted";

interface TrajectoryCanvasProps {
  observed: TrajectorySample[];
  predicted: TrajectorySample[];
  variable: string;
  variableLabel: string;
  /** 0..1, fraction of the predicted series to draw this frame. */
  predictedProgress: number;
  className?: string;
  /** Whether to show the legend (default true). */
  showLegend?: boolean;
}

export function TrajectoryCanvas({
  observed,
  predicted,
  variable,
  variableLabel,
  predictedProgress,
  className,
  showLegend = true,
}: TrajectoryCanvasProps): JSX.Element {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();
    const cssWidth = Math.max(280, rect.width);
    const cssHeight = Math.max(220, rect.height);

    canvas.width = cssWidth * dpr;
    canvas.height = cssHeight * dpr;
    canvas.style.width = `${cssWidth}px`;
    canvas.style.height = `${cssHeight}px`;

    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    ctx.clearRect(0, 0, cssWidth, cssHeight);

    const bounds = computeBounds([observed, predicted], variable);
    const projection = buildProjection(cssWidth, cssHeight, DEFAULT_PADDING, bounds);

    drawAxes(ctx, cssWidth, cssHeight, DEFAULT_PADDING, bounds, variableLabel);

    drawSeries(ctx, observed, variable, projection, {
      color: OBSERVED_COLOR,
      lineWidth: 2,
    });
    drawSeries(ctx, predicted, variable, projection, {
      color: PREDICTED_COLOR,
      lineWidth: 2.5,
      progress: predictedProgress,
    });

    if (showLegend) {
      drawLegend(ctx, cssWidth, cssHeight, predicted.length === 0);
    }
  }, [observed, predicted, predictedProgress, variable, variableLabel, showLegend]);

  return (
    <canvas
      ref={canvasRef}
      className={className ?? "h-[260px] w-full rounded-lg bg-surfaceMuted"}
      role="img"
      aria-label={`${variableLabel} vs time. Observed in cyan; predicted hypothesis in coral.`}
    />
  );
}

function drawLegend(
  ctx: CanvasRenderingContext2D,
  width: number,
  _height: number,
  predictedEmpty: boolean,
): void {
  const padding = 10;
  const swatchW = 14;
  const gap = 8;
  const lineGap = 6;
  ctx.save();
  ctx.font = "11px 'Inter', sans-serif";

  const observedText = LEGEND_OBSERVED_LABEL;
  const predictedText = predictedEmpty
    ? `${LEGEND_PREDICTED_LABEL} (none yet)`
    : LEGEND_PREDICTED_LABEL;
  const observedWidth = ctx.measureText(observedText).width;
  const predictedWidth = ctx.measureText(predictedText).width;
  const boxW =
    swatchW + gap + Math.max(observedWidth, predictedWidth) + padding * 2;
  const boxH = 11 * 2 + lineGap + padding * 2;

  const x = width - boxW - 14;
  const y = 14;

  ctx.fillStyle = "rgba(11,13,16,0.85)";
  ctx.strokeStyle = "rgba(255,255,255,0.08)";
  roundedRect(ctx, x, y, boxW, boxH, 6);
  ctx.fill();
  ctx.stroke();

  ctx.textBaseline = "middle";
  ctx.textAlign = "left";

  // Observed swatch + label
  ctx.fillStyle = OBSERVED_COLOR;
  ctx.fillRect(x + padding, y + padding + 5, swatchW, 2.5);
  ctx.fillStyle = "rgba(229,231,235,0.92)";
  ctx.fillText(observedText, x + padding + swatchW + gap, y + padding + 6);

  // Predicted swatch + label
  ctx.fillStyle = PREDICTED_COLOR;
  ctx.fillRect(
    x + padding,
    y + padding + 11 + lineGap + 5,
    swatchW,
    2.5,
  );
  ctx.fillStyle = predictedEmpty ? "rgba(229,231,235,0.55)" : "rgba(229,231,235,0.92)";
  ctx.fillText(
    predictedText,
    x + padding + swatchW + gap,
    y + padding + 11 + lineGap + 6,
  );

  ctx.restore();
}

function roundedRect(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  w: number,
  h: number,
  r: number,
): void {
  ctx.beginPath();
  ctx.moveTo(x + r, y);
  ctx.lineTo(x + w - r, y);
  ctx.quadraticCurveTo(x + w, y, x + w, y + r);
  ctx.lineTo(x + w, y + h - r);
  ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
  ctx.lineTo(x + r, y + h);
  ctx.quadraticCurveTo(x, y + h, x, y + h - r);
  ctx.lineTo(x, y + r);
  ctx.quadraticCurveTo(x, y, x + r, y);
  ctx.closePath();
}
