export type ToolbarPlacement = "left" | "right" | "bottom";

export interface ToolbarSettings {
  minSize: number;
  maxSize: number;
  lensCount: number;
  gap: number;
}

export interface ToolbarTarget {
  size: number;
  shift: number;
  scale: number;
}

export const NODEDC_TOOLBAR_DEFAULTS = Object.freeze({
  background: "#111115",
  border: "#111117",
  outline: "#1c1c1c",
  minSize: 25,
  maxSize: 87,
  lensCount: 5,
  gap: 10,
  padding: 6,
  hideDelay: 5000,
});

export const NODEDC_TOOLBAR_SPRING = Object.freeze({
  mass: 0.1,
  stiffness: 170,
  damping: 12,
  maxStep: 1 / 240,
});

const BASE_SCALE = 2.25;
const BASE_DISTANCE = 110;
const BASE_NUDGE = 40;
const NUDGE_TIGHTNESS = 0.82;

export function normalizeToolbarSettings({
  minSize = NODEDC_TOOLBAR_DEFAULTS.minSize,
  maxSize = NODEDC_TOOLBAR_DEFAULTS.maxSize,
  lensCount = NODEDC_TOOLBAR_DEFAULTS.lensCount,
  gap = NODEDC_TOOLBAR_DEFAULTS.gap,
}: Partial<ToolbarSettings> = {}): ToolbarSettings {
  const safeMin = Math.max(18, Math.min(42, Number.isFinite(minSize) ? minSize : NODEDC_TOOLBAR_DEFAULTS.minSize));
  const safeMax = Math.max(safeMin, Math.min(88, Number.isFinite(maxSize) ? maxSize : NODEDC_TOOLBAR_DEFAULTS.maxSize));
  const roundedLens = Math.max(1, Math.min(13, Math.round(Number.isFinite(lensCount) ? lensCount : NODEDC_TOOLBAR_DEFAULTS.lensCount)));
  return {
    minSize: safeMin,
    maxSize: safeMax,
    lensCount: roundedLens % 2 === 0 ? Math.min(13, roundedLens + 1) : roundedLens,
    gap: Math.max(0, Number.isFinite(gap) ? gap : NODEDC_TOOLBAR_DEFAULTS.gap),
  };
}

export function stepToolbarSpring(value: number, velocity: number, target: number, dt: number) {
  let nextValue = value;
  let nextVelocity = velocity;
  const duration = Math.min(0.032, Math.max(0.001, dt));
  const steps = Math.max(1, Math.ceil(duration / NODEDC_TOOLBAR_SPRING.maxStep));
  const step = duration / steps;

  for (let index = 0; index < steps; index += 1) {
    const displacement = nextValue - target;
    const springForce = -NODEDC_TOOLBAR_SPRING.stiffness * displacement;
    const dampingForce = -NODEDC_TOOLBAR_SPRING.damping * nextVelocity;
    const acceleration = (springForce + dampingForce) / NODEDC_TOOLBAR_SPRING.mass;
    nextVelocity += acceleration * step;
    nextValue += nextVelocity * step;
  }

  return [nextValue, nextVelocity] as const;
}

export function calculateToolbarTargets(
  centers: readonly number[],
  pointerCoordinate: number,
  settings: ToolbarSettings,
): ToolbarTarget[] {
  const step = settings.minSize + settings.gap;
  const lensRadius = Math.max(
    settings.minSize,
    Math.floor((settings.lensCount - 1) / 2) * step + settings.gap / 2,
  );
  const maxScale = settings.minSize > 0 ? settings.maxSize / settings.minSize : 1;
  const scaledNudge = BASE_NUDGE
    * (lensRadius / BASE_DISTANCE)
    * (Math.max(0, maxScale - 1) / (BASE_SCALE - 1))
    * NUDGE_TIGHTNESS;

  return centers.map((center) => {
    const distance = pointerCoordinate - center;
    const absoluteDistance = Math.abs(distance);
    const scale = absoluteDistance >= lensRadius
      ? 1
      : 1 + (maxScale - 1) * (1 - absoluteDistance / lensRadius);
    return {
      scale,
      size: settings.minSize * scale,
      shift: absoluteDistance > lensRadius
        ? Math.sign(distance || 1) * -scaledNudge
        : (-distance / lensRadius) * scaledNudge * scale,
    };
  });
}
