import { getSecondsBetween } from '@suno/studiokit/timeMapping';
import { describe, expect, it } from 'vitest';

import getTimingFromDownbeats from './getTimingFromDownbeats';

describe('getTimingFromDownbeats', () => {
  describe('Timing Sanity Checks', () => {
    it('produces the correct duration when the last downbeat implies a doubling of tempo', () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [1.5, 3],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 2, timing)).toBe(1.5);
    });
    it('produces the correct duration when tempo is doubled in the middle', () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [1.5, 3],
          [2, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 3, timing)).toBe(2);
    });
    it("produces the correct duration when tempo is 0.66x'd in the middle", () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [2.5, 3],
          [4, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 3, timing)).toBe(4);
    });
    it('produces the correct duration when evaluating a start segment', () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [1.5, 3],
          [2, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 1, timing)).toBe(1);
    });
    it('produces the correct duration when evaluating a middle segment', () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [1.5, 3],
          [2, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(1, 2, timing)).toBe(0.5);
    });
    it('produces the correct duration when evaluating an end segment', () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [1.5, 3],
          [2, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(2, 3, timing)).toBe(0.5);
    });
    it('produces the correct duration when evaluating a larger segment', () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [1.5, 3],
          [2, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 2, timing)).toBe(1.5);
    });
    it('produces the correct duration when timing is reduced', () => {
      const timing = getTimingFromDownbeats(
        [
          [0.0, 1],
          [1.0, 2],
          [2.0, 3],
          [3.0, 4],
          [4.0, 1],
          [4.5, 2],
          [5, 3],
          [5.5, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 7, timing)).toBe(5.5);
    });
    it('produces the correct duration when timing is offset', () => {
      const timing = getTimingFromDownbeats(
        [
          [2.0, 1],
          [3.0, 2],
          [4.0, 3],
          [5.0, 4],
          [6.0, 1],
          [6.5, 2],
          [7, 3],
          [7.5, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 7, timing)).toBe(5.5);
    });
    it('produces the correct duration when evaluating a subset of timing which is offset and later decays to 0.66x', () => {
      const timing = getTimingFromDownbeats(
        [
          [2.0, 1],
          [3.0, 2],
          [4.0, 3],
          [5.0, 4],
          [6.0, 1],
          [7.5, 2],
          [8, 3],
          [8.5, 4],
        ],
        0.0
      );
      expect(getSecondsBetween(0, 4, timing)).toBe(4.0);
    });
  });
});
