/**
 * spring() — Drop-in replacement for Remotion's spring function.
 *
 * Simulates a damped harmonic oscillator (spring physics).
 * Returns a value that animates from 0 → 1 with spring-like motion.
 *
 * API is 1:1 compatible with `import { spring } from 'remotion'`.
 */

export interface SpringConfig {
  /** Friction — higher = less bouncy. Default: 10 */
  damping?: number;
  /** Spring constant — higher = faster. Default: 100 */
  stiffness?: number;
  /** Weight of the object. Default: 1 */
  mass?: number;
  /** Overshoot clamping — if true, value won't exceed 1. Default: false */
  overshootClamping?: boolean;
}

export interface SpringParams {
  /** Current frame (relative to animation start) */
  frame: number;
  /** Frames per second */
  fps: number;
  /** Spring physics configuration */
  config?: SpringConfig;
  /** Starting value. Default: 0 */
  from?: number;
  /** Target value. Default: 1 */
  to?: number;
  /** Delay in frames before animation starts. Default: 0 */
  delay?: number;
}

/**
 * Compute spring value at a given frame.
 *
 * Uses a step-based simulation of a damped spring system:
 *   F = -stiffness * displacement - damping * velocity
 *   acceleration = F / mass
 *
 * @returns Value between `from` and `to` (may overshoot if not clamped)
 */
export function spring(params: SpringParams): number {
  const {
    frame,
    fps,
    config = {},
    from = 0,
    to = 1,
    delay = 0,
  } = params;

  const {
    damping = 10,
    stiffness = 100,
    mass = 1,
    overshootClamping = false,
  } = config;

  // Before delay, return start value
  if (frame < delay) {
    return from;
  }

  const adjustedFrame = frame - delay;

  // Time in seconds for this frame
  const timeInSeconds = adjustedFrame / fps;

  // Simulation parameters
  const STEP_SIZE = 1 / 1000; // 1ms steps for accuracy
  const totalSteps = Math.ceil(timeInSeconds / STEP_SIZE);

  let position = 0; // starts at 0 (normalized, will be mapped to from→to later)
  let velocity = 0;

  for (let i = 0; i < totalSteps; i++) {
    const springForce = -stiffness * (position - 1); // target is 1 (normalized)
    const dampingForce = -damping * velocity;
    const acceleration = (springForce + dampingForce) / mass;

    velocity += acceleration * STEP_SIZE;
    position += velocity * STEP_SIZE;

    // Check for overshoot clamping
    if (overshootClamping) {
      if (position > 1) {
        position = 1;
        velocity = 0;
      }
    }
  }

  // Map from normalized [0→1] to [from→to]
  return from + position * (to - from);
}
