import { IScheduleSettings, ITime, ITimingRule } from '@core/types'
import { nextMonday, set } from 'date-fns'
import { DateUtils } from './date.utils'
import { getTimeSlotsBetweenTime, getTimeSlotsByDate, getTimeSlotsForWeek } from './time-slot'

describe('time slot utils', () => {
	describe('getTimeSlotsBetweenTime', () => {
		const testCases: [Date, Date, number, Date[]][] = [
			[
				setTime({ hours: 6, minutes: 0 }),
				setTime({ hours: 8, minutes: 30 }),
				30,
				[
					setTime({ hours: 6, minutes: 0 }),
					setTime({ hours: 6, minutes: 30 }),
					setTime({ hours: 7, minutes: 0 }),
					setTime({ hours: 7, minutes: 30 }),
					setTime({ hours: 8, minutes: 0 }),
				],
			],
			[
				setTime({ hours: 6, minutes: 0 }),
				setTime({ hours: 8, minutes: 30 }),
				45,
				[setTime({ hours: 6, minutes: 0 }), setTime({ hours: 6, minutes: 45 }), setTime({ hours: 7, minutes: 30 })],
			],
			[
				setTime({ hours: 6, minutes: 0 }),
				setTime({ hours: 9, minutes: 0 }),
				45,
				[
					setTime({ hours: 6, minutes: 0 }),
					setTime({ hours: 6, minutes: 45 }),
					setTime({ hours: 7, minutes: 30 }),
					setTime({ hours: 8, minutes: 15 }),
				],
			],
			[
				setTime({ hours: 6, minutes: 0 }),
				setTime({ hours: 9, minutes: 15 }),
				45,
				[
					setTime({ hours: 6, minutes: 0 }),
					setTime({ hours: 6, minutes: 45 }),
					setTime({ hours: 7, minutes: 30 }),
					setTime({ hours: 8, minutes: 15 }),
				],
			],
		]
		test.each(testCases)('should generate time slots', (start, end, slotLength, slots) => {
			const result = getTimeSlotsBetweenTime(start, end, slotLength)
			expect(result).toEqual(slots)
		})
	})

	describe('getTimeSlotsByDate', () => {
		const testCases: [ITimingRule[], number, Date[]][] = [
			[
				[{ openingTime: { hours: 6, minutes: 0 }, closingTime: { hours: 8, minutes: 30 } }],
				30,
				[
					setTime({ hours: 6, minutes: 0 }),
					setTime({ hours: 6, minutes: 30 }),
					setTime({ hours: 7, minutes: 0 }),
					setTime({ hours: 7, minutes: 30 }),
					setTime({ hours: 8, minutes: 0 }),
				],
			],
			[
				[{ openingTime: { hours: 6, minutes: 0 }, closingTime: { hours: 8, minutes: 30 } }],
				45,
				[setTime({ hours: 6, minutes: 0 }), setTime({ hours: 6, minutes: 45 }), setTime({ hours: 7, minutes: 30 })],
			],
			[
				[{ openingTime: { hours: 6, minutes: 0 }, closingTime: { hours: 9, minutes: 0 } }],
				45,
				[
					setTime({ hours: 6, minutes: 0 }),
					setTime({ hours: 6, minutes: 45 }),
					setTime({ hours: 7, minutes: 30 }),
					setTime({ hours: 8, minutes: 15 }),
				],
			],
			[
				[{ openingTime: { hours: 6, minutes: 0 }, closingTime: { hours: 9, minutes: 15 } }],
				45,
				[
					setTime({ hours: 6, minutes: 0 }),
					setTime({ hours: 6, minutes: 45 }),
					setTime({ hours: 7, minutes: 30 }),
					setTime({ hours: 8, minutes: 15 }),
				],
			],
			[
				[{ openingTime: { hours: 23, minutes: 0 }, closingTime: { hours: 2, minutes: 0 } }],
				60,
				[
					setTime({ hours: 23, minutes: 0 }),
					setDate(2, { hours: 0, minutes: 0 }),
					setDate(2, { hours: 1, minutes: 0 }),
				],
			],
		]
		test.each(testCases)('should generate time slots', (rules, slotLength, slots) => {
			const result = getTimeSlotsByDate(setTime({ hours: 0, minutes: 0 }), rules, slotLength)
			expect(result).toEqual(slots)
		})
	})

	describe('getTimeSlotsForWeek', () => {
		const schedule: IScheduleSettings = {
			weeklySchedule: {
				Monday: {
					closed: false,
					rules: [
						{
							openingTime: { hours: 6, minutes: 0 },
							closingTime: { hours: 8, minutes: 0 },
						},
					],
				},
				Tuesday: {
					closed: false,
					rules: [
						{
							openingTime: { hours: 6, minutes: 0 },
							closingTime: { hours: 8, minutes: 0 },
						},
					],
				},
				Wednesday: {
					closed: false,
					rules: [
						{
							openingTime: { hours: 6, minutes: 0 },
							closingTime: { hours: 8, minutes: 0 },
						},
					],
				},
				Thursday: {
					closed: false,
					rules: [
						{
							openingTime: { hours: 6, minutes: 0 },
							closingTime: { hours: 8, minutes: 0 },
						},
					],
				},
				Friday: {
					closed: false,
					rules: [
						{
							openingTime: { hours: 6, minutes: 0 },
							closingTime: { hours: 8, minutes: 0 },
						},
						{
							openingTime: { hours: 10, minutes: 0 },
							closingTime: { hours: 12, minutes: 0 },
						},
					],
				},
				Saturday: {
					closed: false,
					rules: [
						{
							openingTime: { hours: 6, minutes: 0 },
							closingTime: { hours: 8, minutes: 0 },
						},
					],
				},
				Sunday: {
					closed: true,
					rules: [],
				},
			},
			specialDays: [],
		}
		it('should generate time slots for entire week', () => {
			const result = getTimeSlotsForWeek(nextMonday(setTime({ hours: 0, minutes: 0 })), schedule, 30)
			expect(result).toEqual([
				// Monday
				setDate(6, { hours: 6, minutes: 0 }),
				setDate(6, { hours: 6, minutes: 30 }),
				setDate(6, { hours: 7, minutes: 0 }),
				setDate(6, { hours: 7, minutes: 30 }),
				// Tuesday
				setDate(7, { hours: 6, minutes: 0 }),
				setDate(7, { hours: 6, minutes: 30 }),
				setDate(7, { hours: 7, minutes: 0 }),
				setDate(7, { hours: 7, minutes: 30 }),
				// Wednesday
				setDate(8, { hours: 6, minutes: 0 }),
				setDate(8, { hours: 6, minutes: 30 }),
				setDate(8, { hours: 7, minutes: 0 }),
				setDate(8, { hours: 7, minutes: 30 }),
				// Thursday
				setDate(9, { hours: 6, minutes: 0 }),
				setDate(9, { hours: 6, minutes: 30 }),
				setDate(9, { hours: 7, minutes: 0 }),
				setDate(9, { hours: 7, minutes: 30 }),
				// Friday
				setDate(10, { hours: 6, minutes: 0 }),
				setDate(10, { hours: 6, minutes: 30 }),
				setDate(10, { hours: 7, minutes: 0 }),
				setDate(10, { hours: 7, minutes: 30 }),
				setDate(10, { hours: 10, minutes: 0 }),
				setDate(10, { hours: 10, minutes: 30 }),
				setDate(10, { hours: 11, minutes: 0 }),
				setDate(10, { hours: 11, minutes: 30 }),
				// Saturday
				setDate(11, { hours: 6, minutes: 0 }),
				setDate(11, { hours: 6, minutes: 30 }),
				setDate(11, { hours: 7, minutes: 0 }),
				setDate(11, { hours: 7, minutes: 30 }),
			])
		})
	})
})

function setTime(time: ITime): Date {
	return DateUtils.normalizeSeconds(set(new Date(2025, 0, 1), { ...time }))
}

function setDate(date: number, time: ITime): Date {
	return DateUtils.normalizeSeconds(set(new Date(2025, 0, 1), { date, ...time }))
}
