import { Injectable, inject } from '@angular/core'
import { Interpreter, MachineBuilder, MachineBuilderFactory } from '@core/utils'
import {
	CheckinRequest,
	DashboardTableView,
	DefaultService,
	OrderResponse,
	ReservationResponse,
	TableResponse,
} from '@api-client/angular'
import { HotToastService } from '@ngxpert/hot-toast'
import { BehaviorSubject, finalize } from 'rxjs'
import { ReservationSAM } from './reservation.sam'

@Injectable({ providedIn: 'root' })
export class ReservationSAMService {
	private readonly _toast = inject(HotToastService)
	private readonly _apiService = inject(DefaultService)

	private readonly machineBuilder: MachineBuilder<ReservationSAM.Action, ReservationSAM.State, ReservationSAM.Context>

	private readonly actionLoadingSubject = new BehaviorSubject(false)
	readonly actionLoading$ = this.actionLoadingSubject.asObservable()

	private readonly actionSuccesSubject = new BehaviorSubject<void>(void 0)
	readonly actionSucces$ = this.actionSuccesSubject.asObservable()

	constructor() {
		this.machineBuilder = this.createMachineBuilder()
	}

	createState(
		reservation: ReservationResponse | undefined,
		order: OrderResponse | undefined,
		tables: TableResponse[] | undefined,
		tableState?: DashboardTableView.TableStateEnum,
	): Interpreter<ReservationSAM.Action, ReservationSAM.State, ReservationSAM.Context> {
		const context = { tables, reservation, order }
		const initialState = context.reservation?.state ?? tableState ?? ReservationSAM.getTableState(context)
		return this.machineBuilder.interpret(initialState, context)
	}

	private createMachineBuilder(): MachineBuilder<ReservationSAM.Action, ReservationSAM.State, ReservationSAM.Context> {
		return new MachineBuilderFactory<ReservationSAM.Action, ReservationSAM.State, ReservationSAM.Context>()
			.withActions({
				ConfirmReservation: (context) => this.confirmReservation(context.reservation?.id as string),
				UnConfirmReservation: (context) => this.unConfirmReservation(context.reservation?.id as string),
				RejectReservation: (context) => this.rejectReservation(context.reservation?.id as string),
				Checkin: (context) =>
					this.checkin(
						context.reservation?.tableIds ?? context.tables?.map((table) => table.id),
						context.reservation?.id,
					),
				Checkout: (context) => this.checkout(context.order?.id as string),
				CancelOrder: (context) => this.cancelOrder(context.order?.id as string),
				ReOpenOrder: (context) => this.reOpenOrder(context.order?.id as string),
			})
			.withTransitions(ReservationSAM.DefaultTransitions)
			.build()
	}

	private confirmReservation(id: string): void {
		this.actionLoadingSubject.next(true)
		this._apiService
			.reservationControllerConfirmReservation(id)
			.pipe(finalize(() => this.actionLoadingSubject.next(false)))
			.subscribe({
				next: () => this.actionSuccesSubject.next(),
				error: (error) => this._toast.error(error.message),
			})
	}

	private unConfirmReservation(id: string): void {
		this.actionLoadingSubject.next(true)
		this._apiService
			.reservationControllerUnConfirmReservation(id)
			.pipe(finalize(() => this.actionLoadingSubject.next(false)))
			.subscribe({
				next: () => this.actionSuccesSubject.next(),
				error: (error) => this._toast.error(error.message),
			})
	}

	private rejectReservation(id: string): void {
		this.actionLoadingSubject.next(true)
		this._apiService
			.reservationControllerRejectReservation(id)
			.pipe(finalize(() => this.actionLoadingSubject.next(false)))
			.subscribe({
				next: () => this.actionSuccesSubject.next(),
				error: (error) => this._toast.error(error.message),
			})
	}

	private checkin(tableIds?: string[], reservationId?: string): void {
		this.actionLoadingSubject.next(true)
		const request: CheckinRequest = { tableIds, reservationId }
		this._apiService
			.orderControllerCheckin(request)
			.pipe(finalize(() => this.actionLoadingSubject.next(false)))
			.subscribe({
				next: () => this.actionSuccesSubject.next(),
				error: (error) => this._toast.error(error.message),
			})
	}

	private checkout(orderId: string): void {
		this.actionLoadingSubject.next(true)
		this._apiService
			.orderControllerCheckout(orderId)
			.pipe(finalize(() => this.actionLoadingSubject.next(false)))
			.subscribe({
				next: () => this.actionSuccesSubject.next(),
				error: (error) => this._toast.error(error.message),
			})
	}

	private cancelOrder(id: string): void {
		this.actionLoadingSubject.next(true)
		this._apiService
			.orderControllerCancel(id)
			.pipe(finalize(() => this.actionLoadingSubject.next(false)))
			.subscribe({
				next: () => this.actionSuccesSubject.next(),
				error: (error) => this._toast.error(error.message),
			})
	}

	private reOpenOrder(id: string): void {
		this.actionLoadingSubject.next(true)
		this._apiService
			.orderControllerReOpenOrder(id)
			.pipe(finalize(() => this.actionLoadingSubject.next(false)))
			.subscribe({
				next: () => this.actionSuccesSubject.next(),
				error: (error) => this._toast.error(error.message),
			})
	}
}
