import { Component, inject, viewChild } from '@angular/core'
import { ReservationResponse, ScheduleSettings } from '@api-client/angular'
import { getDaySchedule } from '@core/utils'
import { KadiOverlayModule, OverlayComponent } from '@frontend/shared'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import { AnyType } from '@core/types'
import { addDays, addYears, subDays } from 'date-fns'
import { map, Observable, take } from 'rxjs'
import { RESERVATION_VIEWS, ReservationStore } from './reservation.store'
import {
	ReservationFilter,
	ReservationFilterStates,
	ReservationLayout,
	ReservationLayoutType,
	ReservationView,
} from './reservation.types'
import { TranslocoDirective } from '@jsverse/transloco'
import { MatCalendar } from '@angular/material/datepicker'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { AsyncPipe, DatePipe, NgClass } from '@angular/common'
import { ReservationsListViewComponent } from './layouts/list/reservations-list-view.component'
import { ReservationsGridViewComponent } from './layouts/grid/reservations-grid-view.component'
import { ReservationsCalenderViewComponent } from './layouts/calender/reservations-calender-view.component'
import { SettingsStore } from '../../+state'
import { LucideChevronLeft, LucideChevronRight, LucideDynamicIcon, LucideSearch, LucideX } from '@lucide/angular'

/** Allows the user to view and manage all reservations */
@UntilDestroy({ checkProperties: true })
@Component({
	selector: 'kadi-reservations',
	templateUrl: './reservations.component.html',
	providers: [ReservationStore],
	imports: [
		TranslocoDirective,
		KadiOverlayModule,
		MatCalendar,
		ReactiveFormsModule,
		FormsModule,
		NgClass,
		ReservationsListViewComponent,
		ReservationsGridViewComponent,
		ReservationsCalenderViewComponent,
		AsyncPipe,
		DatePipe,
		LucideChevronRight,
		LucideChevronLeft,
		LucideSearch,
		LucideX,
		LucideDynamicIcon,
	],
})
export class ReservationsComponent {
	readonly _settingsStore = inject(SettingsStore)
	readonly dateOverlay = viewChild.required(OverlayComponent)
	vm$: Observable<{
		reservations: ReservationView[]
		layout: ReservationLayout
		stateFilter: ReservationFilter | undefined
		filterDate: Date
		searchTerm: string | undefined
	}>
	schedule: ScheduleSettings | undefined
	ReservationLayoutType = ReservationLayoutType
	maxDate = addYears(new Date(), 1)
	stateFilterMap: Array<{ label: string; value: string }>
	Layouts = RESERVATION_VIEWS
	private readonly _store = inject(ReservationStore)

	constructor() {
		this.vm$ = this._store.select({
			layout: this._store.layout$,
			stateFilter: this._store.stateFilter$,
			filterDate: this._store.filterDate$,
			searchTerm: this._store.searchTerm$,
			reservations: this._store.reservations$,
		})

		this._store.schedule$.pipe(untilDestroyed(this)).subscribe({
			next: (schedule) => (this.schedule = schedule),
		})

		this.stateFilterMap = Object.keys(ReservationResponse.StateEnum).map((key) => ({
			label: key,
			value: (ReservationResponse.StateEnum as AnyType)[key],
		}))
		this.stateFilterMap.push(
			...Object.keys(ReservationFilterStates).map((key) => ({
				label: key,
				value: (ReservationFilterStates as AnyType)[key],
			})),
		)
	}

	selectFilterDate(date: Date): void {
		this._store.filterByDate(date)
		this.dateOverlay().closeOverlay()
	}

	selectToday(): void {
		const date = this.getNextAvailableDay(new Date(), () => addDays(new Date(), 1))
		this.selectFilterDate(date)
	}

	switchDay(operation: SwitchDateOperation): void {
		this.vm$
			.pipe(
				untilDestroyed(this),
				take(1),
				map((vm) => vm.filterDate),
			)
			.subscribe({
				next: (currentFilter) => {
					const fn = (date: Date) => (operation === 'Next' ? addDays(date, 1) : subDays(date, 1))
					const date = this.getNextAvailableDay(fn(currentFilter), fn)
					this.selectFilterDate(date)
				},
			})
	}

	search(searchTerm: string): void {
		this._store.search(searchTerm)
	}

	setStateFilter(filter: string | undefined): void {
		this._store.setStateFilter(filter as ReservationFilter)
	}

	switchLayout(layout: ReservationLayout): void {
		this._store.selectLayout(layout)
	}

	private getNextAvailableDay(date: Date, nextDayFn: (date: Date) => Date): Date {
		// TODO: Remove never type
		const daySchedule = getDaySchedule(date, this.schedule as never)

		if (daySchedule.closed) {
			const nextDate = nextDayFn(date)
			return this.getNextAvailableDay(nextDate, nextDayFn)
		} else return date
	}
}

type SwitchDateOperation = 'Next' | 'Previous'
