import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { defaultHourForTime, goToToday, getState, setDate, setSelectedHour, todayTargetHour } from '../src/app-state';

function at(hour: number, minute = 0): Date {
  const d = new Date();
  d.setHours(hour, minute, 0, 0);
  return d;
}

describe('defaultHourForTime', () => {
  it.each([
    [0, 30, 'matins'],
    [3, 0, 'matins'],
    [5, 59, 'matins'],
    [6, 0, 'lauds'],
    [6, 30, 'lauds'],
    [7, 59, 'lauds'],
    [8, 0, 'prime'],
    [8, 30, 'prime'],
    [8, 59, 'prime'],
    [9, 0, 'terce'],
    [10, 0, 'terce'],
    [11, 59, 'terce'],
    [12, 0, 'sext'],
    [13, 0, 'sext'],
    [14, 59, 'sext'],
    [15, 0, 'none'],
    [16, 0, 'none'],
    [16, 59, 'none'],
    [17, 0, 'vespers'],
    [18, 0, 'vespers'],
    [20, 59, 'vespers'],
    [21, 0, 'compline'],
    [22, 0, 'compline'],
    [23, 59, 'compline'],
  ] as const)('%i:%i -> %s', (hour, minute, expected) => {
    expect(defaultHourForTime(at(hour, minute))).toBe(expected);
  });
});

describe('goToToday', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date(2026, 7, 28, 10, 0, 0)); // 2026-08-28 10:00 local -> terce
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('jumps to today and preserves the current hour when on a different date', () => {
    setDate('2026-08-01');
    setSelectedHour('vespers');

    goToToday();

    expect(getState().date).toBe('2026-08-28');
    expect(getState().selectedHour).toBe('vespers');
  });

  it('snaps the hour to the current time-of-day default when already on today', () => {
    setDate('2026-08-28');
    setSelectedHour('vespers');

    goToToday();

    expect(getState().date).toBe('2026-08-28');
    expect(getState().selectedHour).toBe('terce');
  });

  it('is idempotent-safe: a second click after jumping to today snaps the hour', () => {
    setDate('2026-08-01');
    setSelectedHour('vespers');

    goToToday(); // -> today, hour still 'vespers'
    goToToday(); // -> already today, snaps hour to time-of-day default

    expect(getState().date).toBe('2026-08-28');
    expect(getState().selectedHour).toBe('terce');
  });
});

describe('todayTargetHour', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date(2026, 7, 28, 10, 0, 0)); // 2026-08-28 10:00 local -> terce
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('matches goToToday, so the Today link href agrees with its click behavior', () => {
    // Different date: href should preserve the current hour, same as goToToday().
    setDate('2026-08-01');
    setSelectedHour('vespers');
    expect(todayTargetHour()).toBe('vespers');

    // Already on today: href should point at the time-of-day default,
    // same as goToToday() — this is what a right-click "open in new tab"
    // relies on, since it follows the href without running the click handler.
    setDate('2026-08-28');
    expect(todayTargetHour()).toBe('terce');
  });
});
