import { Injectable, inject } from '@angular/core'
import { Router } from '@angular/router'
import { NotificationEventType } from '@core/types'
import { WSService } from '@frontend/ws'
import {
	CreateOrderItemRequest,
	DashboardMessageView,
	DefaultService,
	OrderItemResponse,
	PayOrderItemRequest,
	SettingsResponse,
	TableGraphResponse,
	UpdateItemsStateRequest,
} from '@api-client/angular'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import { ComponentStore } from '@ngrx/component-store'
import { Observable, debounceTime, map, switchMap, take, tap } from 'rxjs'
import { ReservationSAMService } from '../reservations/reservation-sam.service'
import { DashboardTable, MenuItem } from './types'

export interface DashboardState {
	tables: DashboardTable[]
	selectedTable: DashboardTable | undefined
	messages: DashboardMessageView[]
	menuItems: MenuItem[]
	settings: SettingsResponse | undefined
	sidebarView: SidebarView
	tabView: TabView
	tableGraph: TableGraphResponse | undefined
}

export type SidebarView = 'Notifications' | 'Order'
export type TabView = 'RoomPlan' | 'Notifications'

@UntilDestroy({ checkProperties: true })
@Injectable()
export class DashboardStore extends ComponentStore<DashboardState> {
	private readonly _router = inject(Router)
	private readonly _wsService = inject(WSService)
	private readonly _apiService = inject(DefaultService)
	private readonly _reservationSAMService = inject(ReservationSAMService)

	constructor() {
		super({
			tables: [],
			selectedTable: undefined,
			messages: [],
			menuItems: [],
			settings: undefined,
			sidebarView: 'Notifications',
			tabView: 'RoomPlan',
			tableGraph: undefined,
		})

		this.loadSettings()
		this.loadTables()
		this.loadMessages()
		this.loadMenuItems()
		this.loadTableGraph()

		this._wsService.messages$.pipe(untilDestroyed(this), debounceTime(200)).subscribe({
			next: ({ event }) => {
				if (event === NotificationEventType.orders_updated || event === NotificationEventType.reservations_updated) {
					this.loadTables()
				} else if (event === NotificationEventType.messages_updated) {
					this.loadMessages()
				}
			},
		})

		this._reservationSAMService.actionSucces$.pipe(untilDestroyed(this), debounceTime(200)).subscribe({
			next: () => this.loadTables(),
		})
	}

	readonly dashboardTables$ = this.select((state) => state.tables)
	readonly dashboardMessages$ = this.select((state) => state.messages)
	readonly selectedTable$ = this.select((state) => state.selectedTable)
	readonly settings$ = this.select((state) => state.settings)
	readonly sidebarView$ = this.select((state) => state.sidebarView)
	readonly tabView$ = this.select((state) => state.tabView)

	selectTable(table: DashboardTable): void {
		const view: SidebarView = table.order ? 'Order' : 'Notifications'
		this.patchState({ selectedTable: table, sidebarView: view })
		this._router.navigateByUrl('/dashboard/table')
	}

	openNotifications(): void {
		this.patchState({ sidebarView: 'Notifications' })
	}

	openOrders(): void {
		this.selectedTable$.pipe(take(1)).subscribe({
			next: (table) => {
				if (table?.order) this.patchState({ sidebarView: 'Order' })
			},
		})
	}

	selectTabView(tabView: TabView): void {
		this.patchState({ tabView })
	}

	payItems(orderId: string, items: PayOrderItemRequest[]): Observable<void> {
		return this._apiService.orderControllerPayItemsViaCash(orderId, items).pipe(tap(() => this.loadTables()))
	}

	updateItemsState(orderId: string, items: OrderItemResponse[], state: OrderItemResponse.StateEnum): void {
		const request: UpdateItemsStateRequest = { state, itemIds: items.map((item) => item.id) }
		this._apiService
			.orderControllerUpdateItemsState(orderId, request)
			.pipe(
				untilDestroyed(this),
				tap(() => this.loadTables()),
			)
			.subscribe()
	}

	updateItemCouse(orderId: string, itemId: string, course: number): void {
		this._apiService
			.orderControllerUpdateItemCourse(orderId, itemId, course)
			.pipe(
				untilDestroyed(this),
				tap(() => this.loadTables()),
			)
			.subscribe()
	}

	addOrderItemsFromCart(orderId: string): Observable<void> {
		return this.state$.pipe(
			take(1),
			map(({ menuItems }) => menuItems),
			switchMap((menuItems) => {
				const orderItems = menuItems
					.filter((item) => item.cartQuantity > 0)
					.map((item) => ({ menuItemId: item.id, quantity: item.cartQuantity }) as CreateOrderItemRequest)

				return this._apiService
					.orderControllerAddOrderItems(orderId, orderItems)
					.pipe(map(() => menuItems))
					.pipe(tap(() => this.loadTables()))
			}),
			// Reset cart quantity to 0
			tap((menuItems) => {
				for (const item of menuItems) {
					item.cartQuantity = 0
				}
				this.patchState({ menuItems })
			}),
			map(() => void 0),
		)
	}

	acknowlegdeMessage(id: string): void {
		this._apiService
			.messageControllerAcknowledge(id)
			.pipe(untilDestroyed(this))
			.subscribe({
				next: () => this.loadMessages(),
			})
	}

	private loadSettings(): void {
		this._apiService
			.settingsControllerFind()
			.pipe(untilDestroyed(this))
			.subscribe({
				next: (settings) => this.patchState({ settings }),
			})
	}

	private loadTableGraph(): void {
		this._apiService
			.roomPlanControllerGetTableGraph()
			.pipe(untilDestroyed(this))
			.subscribe({
				next: (tableGraph) => this.patchState({ tableGraph }),
			})
	}

	private loadTables(): void {
		this._apiService
			.dashboardControllerGetTablesView()
			.pipe(
				untilDestroyed(this),
				map((views) => {
					const tables = views.map((view) => view.tableRef)

					return views.map((view) => {
						const reservationTables = tables.filter((table) => view.reservation?.tableIds.includes(table.id))
						const tableNames = reservationTables.map((table) => table.name)

						return {
							...view,
							tableNames: tableNames.length > 0 ? tableNames : [view.tableRef.name],
							stateService: this._reservationSAMService.createState(view.reservation, view.order, [view.tableRef]),
						}
					})
				}),
			)
			.subscribe({
				next: (tables) => {
					const updatedState: Partial<DashboardState> = { tables }

					const selectedTableId = this.state().selectedTable?.tableRef.id
					if (selectedTableId) {
						// If a table is selected, update it with new data
						const selectedTable = tables.find((_table) => _table.tableRef.id === selectedTableId)!
						updatedState.selectedTable = selectedTable

						// Open Order view if table has an order
						if (selectedTable.order) {
							updatedState.sidebarView = 'Order'
						}
					}

					this.patchState(updatedState)
				},
			})
	}

	private loadMenuItems(): void {
		this._apiService
			.menuControllerFindAllItem()
			.pipe(untilDestroyed(this))
			.subscribe({
				next: (items) => {
					const menuItems = items.map((item) => ({ ...item, cartQuantity: 0 }))
					this.patchState({ menuItems })
				},
			})
	}

	private loadMessages(): void {
		this._apiService
			.dashboardControllerGetMessagesView()
			.pipe(untilDestroyed(this))
			.subscribe({
				next: (messages) => this.patchState({ messages }),
			})
	}
}
