import { describe, expect, it } from 'vitest';

import { UnitMs, formatRelativeTime, getRelativeTimeMagnitude } from './time';

describe('Millisecond units', () => {
  it('passes sanity checks', () => {
    expect(UnitMs.Minute).toBe(60 * UnitMs.Second);
    expect(UnitMs.Hour).toBe(60 * UnitMs.Minute);
    expect(UnitMs.Day).toBe(24 * UnitMs.Hour);
    expect(UnitMs.Week).toBe(7 * UnitMs.Day);
    expect(UnitMs.Year).toBe(52 * UnitMs.Week);
  });
});

describe('formatRelativeTime', () => {
  it('formats a past relative time as expected', () => {
    // Seconds
    expect(formatRelativeTime(-UnitMs.Second)).toBe('1 second ago');
    expect(formatRelativeTime(-10 * UnitMs.Second)).toBe('10 seconds ago');
    expect(formatRelativeTime(-60 * UnitMs.Second)).toBe('60 seconds ago');
    // Minutes
    expect(formatRelativeTime(-60 * UnitMs.Second - 1)).toBe('1 minute ago');
    expect(formatRelativeTime(-90 * UnitMs.Second)).toBe('1 minute ago');
    expect(formatRelativeTime(-90 * UnitMs.Second - 1)).toBe('2 minutes ago');
    expect(formatRelativeTime(-100000)).toBe('2 minutes ago');
    expect(formatRelativeTime(-60 * UnitMs.Minute)).toBe('60 minutes ago');
    // Hours
    expect(formatRelativeTime(-60 * UnitMs.Minute - 1)).toBe('1 hour ago');
    expect(formatRelativeTime(-2 * UnitMs.Hour)).toBe('2 hours ago');
    expect(formatRelativeTime(-3 * UnitMs.Hour)).toBe('3 hours ago');
    expect(formatRelativeTime(-UnitMs.Day)).toBe('24 hours ago');
    // Days
    expect(formatRelativeTime(-UnitMs.Day - 1)).toBe('1 day ago');
    expect(formatRelativeTime(-UnitMs.Week)).toBe('7 days ago');
    // Weeks
    expect(formatRelativeTime(-UnitMs.Week - 1)).toBe('1 week ago');
    expect(formatRelativeTime(-UnitMs.Year)).toBe('52 weeks ago');
    // Years
    expect(formatRelativeTime(-52 * UnitMs.Week - 1)).toBe('1 year ago');
    expect(formatRelativeTime(-UnitMs.Year - 1)).toBe('1 year ago');
  });

  it('formats a future relative time as expected', () => {
    // Seconds
    expect(formatRelativeTime(UnitMs.Second)).toBe('in 1 second');
    expect(formatRelativeTime(10 * UnitMs.Second)).toBe('in 10 seconds');
    expect(formatRelativeTime(60 * UnitMs.Second)).toBe('in 60 seconds');
    // Minutes
    expect(formatRelativeTime(60 * UnitMs.Second + 1)).toBe('in 1 minute');
    expect(formatRelativeTime(90 * UnitMs.Second)).toBe('in 2 minutes');
    expect(formatRelativeTime(90 * UnitMs.Second + 1)).toBe('in 2 minutes');
    expect(formatRelativeTime(100000)).toBe('in 2 minutes');
    expect(formatRelativeTime(60 * UnitMs.Minute)).toBe('in 60 minutes');
    // Hours
    expect(formatRelativeTime(60 * UnitMs.Minute + 1)).toBe('in 1 hour');
    expect(formatRelativeTime(2 * UnitMs.Hour)).toBe('in 2 hours');
    expect(formatRelativeTime(3 * UnitMs.Hour)).toBe('in 3 hours');
    expect(formatRelativeTime(UnitMs.Day)).toBe('in 24 hours');
    // Days
    expect(formatRelativeTime(UnitMs.Day + 1)).toBe('in 1 day');
    expect(formatRelativeTime(UnitMs.Week)).toBe('in 7 days');
    // Weeks
    expect(formatRelativeTime(UnitMs.Week + 1)).toBe('in 1 week');
    expect(formatRelativeTime(UnitMs.Year)).toBe('in 52 weeks');
    // Years
    expect(formatRelativeTime(52 * UnitMs.Week + 1)).toBe('in 1 year');
    expect(formatRelativeTime(UnitMs.Year + 1)).toBe('in 1 year');
  });

  it('accepts "narrow" style', () => {
    expect(formatRelativeTime(-1000, 'narrow')).toBe('1s ago');
    expect(formatRelativeTime(10000, 'narrow')).toBe('in 10s');
    expect(formatRelativeTime(-60000, 'narrow')).toBe('60s ago');
    expect(formatRelativeTime(61000, 'narrow')).toBe('in 1m');
  });

  it('accepts "long" style', () => {
    expect(formatRelativeTime(-1000, 'long')).toBe('1 second ago');
    expect(formatRelativeTime(10000, 'long')).toBe('in 10 seconds');
    expect(formatRelativeTime(-60000, 'long')).toBe('60 seconds ago');
    expect(formatRelativeTime(61000, 'long')).toBe('in 1 minute');
  });

  it('shows "just now" instead of a relative timestamp within the threshold', () => {
    expect(formatRelativeTime(0, 'narrow', 'literally this moment')).toBe(
      'literally this moment'
    );
    expect(formatRelativeTime(0, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(-1000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(-29000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(-30000, 'narrow', 'just now')).toBe('30s ago');
    expect(formatRelativeTime(-31000, 'narrow', 'just now')).toBe('31s ago');
    expect(formatRelativeTime(1000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(29000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(30000, 'narrow', 'just now')).toBe('in 30s');
    expect(formatRelativeTime(31000, 'narrow', 'just now')).toBe('in 31s');
  });

  it('supports a custom "just now" threshold of a relative timestamp within the threshold', () => {
    expect(formatRelativeTime(0, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(-1000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(-29000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(-30000, 'narrow', 'just now')).toBe('30s ago');
    expect(formatRelativeTime(-31000, 'narrow', 'just now')).toBe('31s ago');
    expect(formatRelativeTime(1000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(29000, 'narrow', 'just now')).toBe('just now');
    expect(formatRelativeTime(30000, 'narrow', 'just now')).toBe('in 30s');
    expect(formatRelativeTime(31000, 'narrow', 'just now')).toBe('in 31s');
  });
});

describe('getRelativeTimeMagnitude', () => {
  it('gets relative magnitude of a past offset', () => {
    expect(getRelativeTimeMagnitude(-1000)).toEqual([-1, 'seconds']);
  });
  it('gets relative magnitude of a future offset', () => {
    expect(getRelativeTimeMagnitude(1000)).toEqual([1, 'seconds']);
  });
  it('works as expected when there is no offset', () => {
    expect(getRelativeTimeMagnitude(0)).toEqual([0, 'seconds']);
  });

  it('handles equivalent magnitudes as expected', () => {
    expect(getRelativeTimeMagnitude(-90 * UnitMs.Second)).toEqual([
      -1,
      'minutes',
    ]);
    expect(getRelativeTimeMagnitude(90 * UnitMs.Second)).toEqual([
      2,
      'minutes',
    ]);
  });
});
