import { expect, test } from 'bun:test';
import { addDays, endOfDay, localDate, scheduled, schedulesOverlap, shade } from './calendar';
import { calendarSettingsSchema, dateSchema, habitInput, scheduleSchema } from './contracts';
test('fixed calendar recurrence uses anchors and Monday weeks independently of completions', () => {
  expect(scheduled({ type: 'daily' }, '2026-09-04')).toBe(true);
  expect(scheduled({ type: 'interval', every: 3, anchor: '2026-09-04' }, '2026-09-07')).toBe(true);
  expect(scheduled({ type: 'interval', every: 3, anchor: '2026-09-04' }, '2026-09-01')).toBe(false);
  expect(scheduled({ type: 'interval', every: 3, anchor: '2026-09-04' }, '2026-09-06')).toBe(false);
  expect(scheduled({ type: 'weekdays', days: [1] }, '2026-09-07')).toBe(true);
  expect(scheduled({ type: 'weekdays', days: [1] }, '2026-09-08')).toBe(false);
  const rule = { type: 'weekly' as const, every: 2, weekday: 3, anchor: '2026-08-31' };
  expect(scheduled(rule, '2026-09-02')).toBe(true);
  expect(scheduled(rule, '2026-09-09')).toBe(false);
  expect(scheduled(rule, '2026-09-16')).toBe(true);
});
test('real dates, strict settings, and exclusive methods are validated', () => {
  for (const d of ['2026-02-29', '2026-13-01', '2026-9-1', 'garbage']) expect(dateSchema.safeParse(d).success).toBe(false);
  expect(addDays('2024-02-28', 1)).toBe('2024-02-29');
  expect(habitInput.safeParse({ name: 'mixed', method: 'manual', target: 8 }).success).toBe(false);
  expect(scheduleSchema.safeParse({ type: 'weekdays', days: [1, 1] }).success).toBe(false);
  expect(calendarSettingsSchema.safeParse({ daySpacing: 4 }).success).toBe(false);
});
test('local midnight handles spring, autumn, and fractional timezone offsets', () => {
  expect(endOfDay('2026-03-29', 'Europe/Belgrade') - endOfDay('2026-03-28', 'Europe/Belgrade')).toBe(23 * 3600000);
  expect(endOfDay('2026-10-25', 'Europe/Belgrade') - endOfDay('2026-10-24', 'Europe/Belgrade')).toBe(25 * 3600000);
  expect(new Date(endOfDay('2026-09-04', 'Asia/Kathmandu')).toISOString()).toBe('2026-09-04T18:15:00.000Z');
  expect(localDate(Date.parse('2026-09-03T22:00Z'), 'Europe/Belgrade')).toBe('2026-09-04');
});
test('eight positive count shades, full completion, neutral and empty stay distinct', () => {
  const settings = calendarSettingsSchema.parse({});
  const values = Array.from({ length: 8 }, (_, i) => shade((i + 1) / 8, 8, settings, true));
  expect(new Set(values.map(v => v.color)).size).toBe(8);
  expect(values.map(v => v.level)).toEqual([1,2,3,4,5,6,7,8]);
  expect(shade(1, 1, settings, true).color).toBe(settings.mainColor);
  expect(shade(0, 8, settings, true).color).toBe(settings.emptyColor);
  expect(shade(null, 8, settings, true).color).toBe(settings.notDueColor);
  expect(shade(0.75, 4, settings, false).level).toBe(3);
});
test('white and pale main colors still produce distinct positive shades', () => {
  for (const mainColor of ['#ffffff', '#fefdfc', '#ffff00', '#000000']) {
    const settings = calendarSettingsSchema.parse({ mainColor, shadeCount: 20 });
    const colors = Array.from({ length: 20 }, (_, i) => shade((i + 1) / 20, 20, settings, true).color);
    expect(new Set(colors).size).toBe(20); expect(colors.at(-1)).toBe(mainColor);
  }
});

test('schedule intersection detects incompatible anchored day and week cycles', () => {
  expect(schedulesOverlap({ type: 'interval', every: 7, anchor: '2026-09-04' }, { type: 'weekdays', days: [1] })).toBe(false);
  expect(schedulesOverlap({ type: 'interval', every: 3, anchor: '2026-09-04' }, { type: 'weekdays', days: [1] })).toBe(true);
  expect(schedulesOverlap({ type: 'weekly', every: 2, weekday: 3, anchor: '2026-08-31' }, { type: 'weekly', every: 2, weekday: 3, anchor: '2026-09-07' })).toBe(false);
  expect(schedulesOverlap({ type: 'weekly', every: 2, weekday: 3, anchor: '2026-08-31' }, { type: 'weekly', every: 3, weekday: 3, anchor: '2026-09-07' })).toBe(true);
  expect(schedulesOverlap({ type: 'daily' }, { type: 'weekly', every: 520, weekday: 0, anchor: '2026-09-04' })).toBe(true);
});
