import { describe, it, expect } from 'vitest';
import { dayBucket, groupByDay } from './dayGroup';
import type { HistoryEvent } from '$lib/api/history';
import { makeTrack } from '$test-utils/fixtures/track';

const NOW = new Date('2026-05-04T14:00:00Z');

const stubTrack = makeTrack();

const mkEvent = (id: string, playedAt: string): HistoryEvent => ({
  id,
  played_at: playedAt,
  track: stubTrack
});

describe('dayBucket', () => {
  it('5 minutes ago → Today', () => {
    expect(dayBucket(new Date('2026-05-04T13:55:00Z'), NOW)).toBe('Today');
  });

  it('today at 00:01 → Today (when now is later same day)', () => {
    expect(dayBucket(new Date('2026-05-04T00:01:00Z'), NOW)).toBe('Today');
  });

  it('yesterday at 23:59 → Yesterday', () => {
    expect(dayBucket(new Date('2026-05-03T23:59:00Z'), NOW)).toBe('Yesterday');
  });

  it('6 days ago at 12:00 → This week', () => {
    expect(dayBucket(new Date('2026-04-28T12:00:00Z'), NOW)).toBe('This week');
  });

  it('7 days ago → absolute date (current year, no year suffix)', () => {
    const result = dayBucket(new Date('2026-04-27T12:00:00Z'), NOW);
    // Expect format like "Apr 27" (no year suffix); regex avoids exact match
    // because Intl.DateTimeFormat output may vary slightly across runtimes.
    expect(result).toMatch(/^[A-Za-z]+ \d+$/);
  });

  it('different calendar year → absolute date with year', () => {
    const result = dayBucket(new Date('2025-12-25T12:00:00Z'), NOW);
    expect(result).toMatch(/2025/);
  });
});

describe('groupByDay', () => {
  it('returns empty array for empty input', () => {
    expect(groupByDay([], NOW)).toEqual([]);
  });

  it('emits one section per bucket transition, preserves order', () => {
    const events = [
      mkEvent('e1', '2026-05-04T13:00:00Z'),
      mkEvent('e2', '2026-05-04T11:00:00Z'),
      mkEvent('e3', '2026-05-03T20:00:00Z'),
      mkEvent('e4', '2026-04-28T10:00:00Z')
    ];
    const groups = groupByDay(events, NOW);
    expect(groups.map((g) => g.label)).toEqual(['Today', 'Yesterday', 'This week']);
    expect(groups[0].events.map((e) => e.id)).toEqual(['e1', 'e2']);
    expect(groups[1].events.map((e) => e.id)).toEqual(['e3']);
    expect(groups[2].events.map((e) => e.id)).toEqual(['e4']);
  });

  it('does not collapse events across same bucket if input order changes', () => {
    // Defensive: walker is stateful per-iteration; out-of-order input
    // would emit duplicate buckets. Real input is always sorted, but
    // pin the contract.
    const events = [
      mkEvent('e1', '2026-05-04T13:00:00Z'), // Today
      mkEvent('e2', '2026-05-03T20:00:00Z'), // Yesterday
      mkEvent('e3', '2026-05-04T11:00:00Z')  // Today (out of order)
    ];
    const groups = groupByDay(events, NOW);
    expect(groups.map((g) => g.label)).toEqual(['Today', 'Yesterday', 'Today']);
    expect(groups[0].events.map((e) => e.id)).toEqual(['e1']);
    expect(groups[2].events.map((e) => e.id)).toEqual(['e3']);
  });
});
