import assert from 'node:assert/strict';
import test from 'node:test';

import {
  findNearestAheadDistance,
  normalizeTokenPath,
  reverseTokenPathPoints,
  rotateTokenPathByCwSteps,
  sampleTokenPathAtProgress,
  tokenPathTotalLength,
  tryAppendPathPoint,
  TOKEN_PATH_MIN_POINT_DIST,
} from './tokenPath';

void test('tryAppendPathPoint rejects double-click distance', () => {
  const a = tryAppendPathPoint([], { nx: 0.1, ny: 0.1 });
  assert.ok(a);
  const b = tryAppendPathPoint(a!, { nx: 0.1 + TOKEN_PATH_MIN_POINT_DIST / 2, ny: 0.1 });
  assert.equal(b, null);
  const c = tryAppendPathPoint(a!, { nx: 0.5, ny: 0.5 });
  assert.equal(c?.length, 2);
});

void test('normalizeTokenPath requires ≥2 points', () => {
  assert.equal(normalizeTokenPath({ points: [{ nx: 0.1, ny: 0.1 }] }), null);
  const p = normalizeTokenPath({
    points: [
      { nx: 0.1, ny: 0.1 },
      { nx: 0.5, ny: 0.1 },
    ],
    durationSec: 10,
    closed: true,
    loopMode: 'loop',
  });
  assert.ok(p);
  assert.equal(p!.loopMode, 'loop');
  assert.equal(p!.closed, true);
});

void test('loop forced to once when not closed', () => {
  const p = normalizeTokenPath({
    points: [
      { nx: 0, ny: 0 },
      { nx: 1, ny: 0 },
    ],
    closed: false,
    loopMode: 'loop',
  });
  assert.ok(p);
  assert.equal(p!.loopMode, 'once');
});

void test('sampleTokenPathAtProgress endpoints', () => {
  const p = normalizeTokenPath({
    points: [
      { nx: 0, ny: 0.5 },
      { nx: 1, ny: 0.5 },
    ],
    durationSec: 5,
    facingMode: 'fixed',
    fixedRotationDeg: 45,
  });
  assert.ok(p);
  const a = sampleTokenPathAtProgress(p!, 0);
  const b = sampleTokenPathAtProgress(p!, 1);
  assert.ok(a && b);
  assert.ok(Math.abs(a!.nx - 0) < 1e-6);
  assert.ok(Math.abs(b!.nx - 1) < 1e-6);
  assert.equal(a!.rotationDeg, 45);
});

void test('reverse and rotate path', () => {
  const pts = reverseTokenPathPoints([
    { nx: 0.1, ny: 0.2 },
    { nx: 0.8, ny: 0.2 },
  ]);
  assert.deepEqual(pts[0], { nx: 0.8, ny: 0.2 });
  const path = normalizeTokenPath({
    points: [
      { nx: 0.2, ny: 0.1 },
      { nx: 0.8, ny: 0.1 },
    ],
    fixedRotationDeg: 10,
  })!;
  const rotated = rotateTokenPathByCwSteps(path, 1);
  assert.ok(Math.abs(rotated.points[0]!.nx - 0.9) < 1e-6);
  assert.ok(Math.abs(rotated.points[0]!.ny - 0.2) < 1e-6);
  assert.equal(rotated.fixedRotationDeg, 100);
  assert.ok(tokenPathTotalLength(path) > 0);
});

void test('findNearestAheadDistance prefers ahead of fromDist', () => {
  const path = normalizeTokenPath({
    points: [
      { nx: 0, ny: 0.5 },
      { nx: 1, ny: 0.5 },
    ],
  })!;
  const mid = findNearestAheadDistance(path, 0.7, 0.5, 0.2);
  assert.ok(mid > 0.2);
});
