// Pure canvas painters. No React, no DOM events. Re-painted only when peaks
// change, the container resizes, or theme tokens change. See ADR-003.

import { backingHeight, backingWidth, getDpr } from '../../utils/dpr';

export type PaintPeaksOptions = {
  color: string;
  background?: string;
  barWidth: number;
  barGap: number;
  minBarHeight?: number;
};

function resizeCanvas(canvas: HTMLCanvasElement): { ctx: CanvasRenderingContext2D; cssW: number; cssH: number } | null {
  const cssW = canvas.clientWidth;
  const cssH = canvas.clientHeight;
  if (cssW === 0 || cssH === 0) return null;
  const dpr = getDpr();
  const w = backingWidth(cssW, dpr);
  const h = backingHeight(cssH, dpr);
  if (canvas.width !== w) canvas.width = w;
  if (canvas.height !== h) canvas.height = h;
  const ctx = canvas.getContext('2d', { alpha: true, desynchronized: true });
  if (!ctx) return null;
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  return { ctx, cssW, cssH };
}

export function paintPeaks(
  canvas: HTMLCanvasElement,
  peaks: Float32Array,
  opts: PaintPeaksOptions,
): void {
  const sized = resizeCanvas(canvas);
  if (!sized) return;
  const { ctx, cssW, cssH } = sized;
  ctx.clearRect(0, 0, cssW, cssH);
  if (opts.background) {
    ctx.fillStyle = opts.background;
    ctx.fillRect(0, 0, cssW, cssH);
  }

  if (peaks.length === 0) return;
  const step = Math.max(1, opts.barWidth + opts.barGap);
  const numBars = Math.max(1, Math.floor(cssW / step));
  const mid = cssH / 2;
  const minH = opts.minBarHeight ?? 1;

  ctx.fillStyle = opts.color;
  for (let i = 0; i < numBars; i++) {
    const peakIdx = Math.min(peaks.length - 1, Math.floor((i / numBars) * peaks.length));
    const amp = peaks[peakIdx];
    const h = Math.max(minH, amp * cssH);
    const x = i * step;
    ctx.fillRect(x, mid - h / 2, opts.barWidth, h);
  }
}

export type PaintLiveOptions = {
  color: string;
  barWidth: number;
  barGap: number;
  minBarHeight?: number;
};

export function paintLive(
  canvas: HTMLCanvasElement,
  levels: Float32Array,
  opts: PaintLiveOptions,
): void {
  const sized = resizeCanvas(canvas);
  if (!sized) return;
  const { ctx, cssW, cssH } = sized;
  ctx.clearRect(0, 0, cssW, cssH);
  if (levels.length === 0) return;

  const step = Math.max(1, opts.barWidth + opts.barGap);
  const numBars = Math.max(1, Math.floor(cssW / step));
  const mid = cssH / 2;
  const minH = opts.minBarHeight ?? 1;
  // Concentrate visible energy in the lower frequency bands.
  const usable = Math.floor(levels.length * 0.7);

  ctx.fillStyle = opts.color;
  for (let i = 0; i < numBars; i++) {
    const idx = Math.min(usable - 1, Math.floor((i / numBars) * usable));
    const v = levels[idx] ?? 0;
    const h = Math.max(minH, v * cssH);
    ctx.fillRect(i * step, mid - h / 2, opts.barWidth, h);
  }
}
