import { beforeEach, describe, expect, it, vi } from 'vitest';
import { MockLoop, MockTransport } from '../../test/mocks/tone';

vi.mock('tone', async () => {
  const { ToneMock: toneMock } = await import('../../test/mocks/tone');
  return toneMock;
});

import { KGMetronome } from './KGMetronome';

describe('KGMetronome', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(0);
    vi.clearAllMocks();
    MockTransport.bpm.value = 120;
    MockTransport.getTicksAtTime.mockImplementation((time: number) => time * MockTransport.PPQ);
  });

  it('schedules audible preroll clicks before beat 0 and keeps the beat 0 accent', () => {
    const metronome = new KGMetronome();
    const triggerAttackRelease = vi.fn()
    ;(metronome as unknown as { sampler: unknown }).sampler = {
      loaded: true,
      triggerAttackRelease,
    };

    metronome.start(-4, 4, 0.2);

    vi.advanceTimersByTime(200);
    expect(triggerAttackRelease.mock.calls[0]?.[0]).toBe('C5');
    expect(triggerAttackRelease.mock.calls[0]?.[1]).toBe('16n');
    expect(triggerAttackRelease.mock.calls[0]?.[2]).toBeCloseTo(0.2, 5);

    vi.advanceTimersByTime(1000);
    expect(triggerAttackRelease.mock.calls[1]?.[0]).toBe('C4');
    expect(triggerAttackRelease.mock.calls[1]?.[1]).toBe('16n');
    expect(triggerAttackRelease.mock.calls[1]?.[2]).toBeCloseTo(0.7, 5);

    const transportLoop = MockLoop.mock.results[0]?.value;
    expect(transportLoop).toBeDefined();

    transportLoop.callback(0);
    expect(triggerAttackRelease).toHaveBeenLastCalledWith('C5', '16n', 0.2);
  });

  it('cancels pending preroll clicks when stopped', () => {
    const metronome = new KGMetronome();
    const triggerAttackRelease = vi.fn()
    ;(metronome as unknown as { sampler: unknown }).sampler = {
      loaded: true,
      triggerAttackRelease,
    };

    metronome.start(-2, 4, 0.2);
    metronome.stop();
    vi.runAllTimers();

    expect(triggerAttackRelease).not.toHaveBeenCalled();
  });
});
