import { TestBed } from '@automock/jest'
import { Reservation } from '@backend/domain'
import { ReservationRepository } from '@backend/repository'
import { ReservationState } from '@core/types'
import { FindManyOptions, FindOperator } from 'typeorm'
import { ReservationStatsService } from './reservation-stats.service'

describe('ReservationStatsService', () => {
	let service: ReservationStatsService
	let repository: jest.Mocked<ReservationRepository>

	beforeAll(() => {
		const { unit, unitRef } = TestBed.create(ReservationStatsService).compile()
		service = unit
		repository = unitRef.get(ReservationRepository)
	})

	beforeEach(() => jest.clearAllMocks())

	it('counts reservations and sums guests', async () => {
		repository.find.mockResolvedValue([
			{ noOfPersons: 2 },
			{ noOfPersons: 4 },
			{ noOfPersons: 6 },
		] as Reservation[])

		const stats = await service.getDayStats(new Date('2026-08-20T15:30:00Z'), 'Europe/Berlin')

		expect(stats).toEqual({ reservationCount: 3, guestCount: 12 })
	})

	it('returns zeros for a day without reservations', async () => {
		repository.find.mockResolvedValue([])

		const stats = await service.getDayStats(new Date('2026-08-20T15:30:00Z'), 'Europe/Berlin')

		expect(stats).toEqual({ reservationCount: 0, guestCount: 0 })
	})

	it('excludes rejected reservations via the state filter', async () => {
		repository.find.mockResolvedValue([])

		await service.getDayStats(new Date('2026-08-20T15:30:00Z'), 'Europe/Berlin')

		const options = repository.find.mock.calls[0][0] as FindManyOptions<Reservation>
		const stateFilter = (options.where as { state: FindOperator<ReservationState> }).state
		expect(stateFilter.value).toEqual(
			expect.arrayContaining([
				ReservationState.unconfirmed,
				ReservationState.approved,
				ReservationState.seated,
				ReservationState.done,
			]),
		)
		expect(stateFilter.value).not.toEqual(expect.arrayContaining([ReservationState.rejected]))
	})

	it('computes the day window in the restaurant timezone, not UTC', async () => {
		repository.find.mockResolvedValue([])

		// 23:30 Berlin local time on Aug 20 = 21:30 UTC; the Berlin day runs
		// from Aug 19 22:00 UTC to Aug 20 22:00 UTC
		await service.getDayStats(new Date('2026-08-20T21:30:00Z'), 'Europe/Berlin')

		const options = repository.find.mock.calls[0][0] as FindManyOptions<Reservation>
		const startFilter = (options.where as { startDate: FindOperator<Date> }).startDate
		// And-operator: [MoreThanOrEqual(dayStart), LessThan(dayEnd)]
		const [gte, lt] = startFilter.value as unknown as [FindOperator<Date>, FindOperator<Date>]
		expect(new Date(gte.value as unknown as Date).toISOString()).toBe('2026-08-19T22:00:00.000Z')
		expect(new Date(lt.value as unknown as Date).toISOString()).toBe('2026-08-20T22:00:00.000Z')
	})

	it('assigns a late-evening reservation to the correct local day', async () => {
		repository.find.mockResolvedValue([])

		// 00:30 Berlin local on Aug 21 = Aug 20 22:30 UTC → local day is Aug 21
		await service.getDayStats(new Date('2026-08-20T22:30:00Z'), 'Europe/Berlin')

		const options = repository.find.mock.calls[0][0] as FindManyOptions<Reservation>
		const startFilter = (options.where as { startDate: FindOperator<Date> }).startDate
		const [gte] = startFilter.value as unknown as [FindOperator<Date>]
		expect(new Date(gte.value as unknown as Date).toISOString()).toBe('2026-08-20T22:00:00.000Z')
	})
})
