import { ReservationRepository } from '@backend/repository'
import { ReservationState } from '@core/types'
import { TZDate } from '@date-fns/tz'
import { Injectable } from '@nestjs/common'
import { addDays } from 'date-fns'
import { sumBy } from 'es-toolkit/compat'
import { And, In, LessThan, MoreThanOrEqual } from 'typeorm'
import { ReservationDayStats } from '../types'

/**
 * States that occupy (or occupied) the restaurant on a given day.
 * Everything except rejected/cancelled counts toward the day stats.
 */
const DAY_STATS_STATES = [
	ReservationState.unconfirmed,
	ReservationState.approved,
	ReservationState.seated,
	ReservationState.done,
]

@Injectable()
export class ReservationStatsService {
	constructor(private readonly _reservationRepository: ReservationRepository) {}

	/**
	 * Counts reservations and guests for the restaurant-local day that contains the
	 * given date. The day boundary is computed in the restaurant's timezone, not in
	 * server or UTC time, so late-evening reservations land on the correct day.
	 *
	 * @param date Any point in time inside the requested day (usually reservation.startDate)
	 * @param timezone IANA timezone of the restaurant (settings.restaurant.timezone)
	 */
	async getDayStats(date: Date, timezone: string): Promise<ReservationDayStats> {
		const zoned = new TZDate(date, timezone)
		const dayStart = new TZDate(zoned.getFullYear(), zoned.getMonth(), zoned.getDate(), 0, 0, 0, timezone)
		const dayEnd = addDays(dayStart, 1)

		const reservations = await this._reservationRepository.find({
			where: {
				state: In(DAY_STATS_STATES),
				startDate: And(MoreThanOrEqual(dayStart), LessThan(dayEnd)),
			},
			select: { noOfPersons: true },
		})

		return {
			reservationCount: reservations.length,
			guestCount: sumBy(reservations, 'noOfPersons'),
		}
	}
}
