package org.hirw.game.util;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class TimeTest {
  final float ONE_SECOND = 1_000_000_000f;
  final long ONE_MILLISECOND = 1_000_000L;
  final float MOE = 0.00000001f; // Margin of error

  private Time.NanoTimer nanoTimer;

  @BeforeEach
  void setUp() {
    nanoTimer = Mockito.mock(Time.NanoTimer.class);
    when(nanoTimer.nanoTime()).thenReturn(0L);
    Time.setNanoTimer(nanoTimer);
  }

  @Test
  void testDeltaTimeInitialState() {
    float deltaTime = Time.deltaTime();
    assertEquals(deltaTime, 0f, MOE, "should be 0");
  }

  @Test
  void testDeltaTimeAfterUpdate() {
    when(nanoTimer.nanoTime()).thenReturn(ONE_MILLISECOND);

    Time.update();
    float deltaTime = Time.deltaTime();

    assertEquals(deltaTime, ONE_MILLISECOND / ONE_SECOND, MOE, "should reflect time difference");
  }

  @Test
  void testMultipleUpdates() {
    when(nanoTimer.nanoTime()).thenReturn(ONE_MILLISECOND).thenReturn(ONE_MILLISECOND * 4);

    Time.update();
    float delta1 = Time.deltaTime();

    Time.update();
    float delta2 = Time.deltaTime();

    assertEquals(delta1, ONE_MILLISECOND / ONE_SECOND, MOE, "should be 1/1000th second");
    assertEquals(delta2, (ONE_MILLISECOND * 3) / ONE_SECOND, MOE, "should be 3/1000th second");
  }

  @Test
  void testConsistentStateAcrossCalls() {
    when(nanoTimer.nanoTime()).thenReturn(ONE_MILLISECOND);

    Time.update();
    float delta1 = Time.deltaTime();
    float delta2 = Time.deltaTime();

    assertEquals(delta1, delta2, MOE, "should be equal delta time");
  }
}
