import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, forwardRef, input } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { ITimeRange } from '@core/types'
import { TimeUtils } from '@core/utils'
import { Time, TimeSlot } from '@api-client/angular'
import { range } from 'es-toolkit'
import { NgxSkeletonLoaderConfigTheme, NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'
import { TimeFormatPipe } from '../../pipes'

@Component({
	selector: 'kadi-time-slot-picker',
	templateUrl: './time-slot-picker.component.html',
	styleUrls: ['./time-slot-picker.component.scss'],
	changeDetection: ChangeDetectionStrategy.OnPush,
	imports: [CommonModule, TimeFormatPipe, NgxSkeletonLoaderModule],
	providers: [
		{
			provide: NG_VALUE_ACCESSOR,
			useExisting: forwardRef(() => TimeSlotPickerComponent),
			multi: true,
		},
	],
})
export class TimeSlotPickerComponent implements ControlValueAccessor {
	readonly slots = input<TimeSlot[]>([])
	readonly defaultTime = input<number>()
	readonly loading = input(false)
	readonly useEndTime = input(true)

	readonly range = range

	selectedTime: ITimeRange = { start: undefined, end: undefined }
	hoveredSlot: Time | undefined

	skeletonTheme: NgxSkeletonLoaderConfigTheme = {
		'border-radius': '12px',
		width: '64px',
		height: '28px',
	}

	/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */
	onChange = (_time: ITimeRange) => {}
	onTouched = () => {}
	onValidatorChange = () => {}
	/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */

	//#region ControlValueAccessor method implementation
	writeValue(time: ITimeRange | null | undefined): void {
		if (time === null || time === undefined)
			this.selectTime({
				start: undefined,
				end: undefined,
			})
		else this.selectTime(time)
	}

	registerOnChange(onChange: (_time: ITimeRange | undefined) => void): void {
		this.onChange = onChange
	}

	registerOnTouched(onTouched: () => void): void {
		this.onTouched = onTouched
	}
	//#endregion

	onClickSlot(time: Time): void {
		// If end time behavior is disabled, handle only start time
		if (!this.useEndTime()) {
			this.selectedTime = {
				start: time,
				end: undefined,
			} // Clear end time
			const defaultTime = this.defaultTime()
			if (defaultTime) this.selectDefaultEndTime(time, defaultTime)
			this.selectTime(this.selectedTime)
			return
		}

		// Existing logic for end time selection
		if (this.selectedTime.end) {
			// If last slot
			if (time === this.slots()[this.slots().length - 1]?.time) return
			this.selectedTime.start = time
			this.selectedTime.end = undefined
		} else if (this.selectedTime.start) {
			const startTimeIndex = this.slots().findIndex((_slot) => TimeUtils.isEqual(_slot.time, this.selectedTime.start!))
			const timeIndex = this.slots().findIndex((_slot) => TimeUtils.isEqual(_slot.time, time))

			if (timeIndex > startTimeIndex) {
				this.selectedTime.end = time
			} else {
				this.selectedTime.start = time
				this.selectedTime.end = undefined
			}
		} else {
			if (time === this.slots()[this.slots().length - 1]?.time) return
			this.selectedTime.start = time
			const defaultTime = this.defaultTime()
			if (defaultTime) {
				this.selectDefaultEndTime(this.selectedTime.start, defaultTime)
			}
		}
		this.selectTime(this.selectedTime)
	}

	onHoverSlot(time: Time): void {
		this.hoveredSlot = this.selectedTime.start !== undefined && this.selectedTime.end === undefined ? time : undefined
	}

	isActiveSlot(slot: Time): boolean {
		if (!this.useEndTime()) {
			return this.selectedTime.start ? TimeUtils.isEqual(slot, this.selectedTime.start) : false
		}
		return (
			(this.selectedTime.start ? TimeUtils.isEqual(slot, this.selectedTime.start) : false) ||
			(this.selectedTime.end ? TimeUtils.isEqual(slot, this.selectedTime.end) : false)
		)
	}

	isFirstInRange(time: Time): boolean {
		if (!this.selectedTime.start || !this.selectedTime.end || !this.useEndTime()) return false

		const startTimeIndex = this.slots().findIndex((slot) => TimeUtils.isEqual(slot.time, this.selectedTime.start!))
		const timeIndex = this.slots().findIndex((slot) => TimeUtils.isEqual(slot.time, time))

		return timeIndex === startTimeIndex
	}

	isLastInRange(time: Time): boolean {
		if (!this.selectedTime.start || !this.selectedTime.end || !this.useEndTime()) return false

		const endTimeIndex = this.slots().findIndex((slot) => TimeUtils.isEqual(slot.time, this.selectedTime.end!))
		const timeIndex = this.slots().findIndex((slot) => TimeUtils.isEqual(slot.time, time))

		return timeIndex === endTimeIndex
	}

	isSingleSlot(): boolean {
		if (!this.useEndTime()) return true
		return (this.selectedTime.start && !this.selectedTime.end) ?? false
	}

	isInRange(time: Time): boolean {
		if (!this.selectedTime.start || !this.selectedTime.end || !this.useEndTime()) return false

		const startTimeIndex = this.slots().findIndex((slot) => TimeUtils.isEqual(slot.time, this.selectedTime.start!))
		const endTimeIndex = this.slots().findIndex((slot) => TimeUtils.isEqual(slot.time, this.selectedTime.end!))
		const timeIndex = this.slots().findIndex((slot) => TimeUtils.isEqual(slot.time, time))

		return timeIndex > startTimeIndex && timeIndex < endTimeIndex
	}

	private selectTime(time: ITimeRange): void {
		this.onTouched()
		this.selectedTime = time
		this.onChange(this.selectedTime)
	}

	private selectDefaultEndTime(startTime: Time, defaultTime: number): void {
		const end = TimeUtils.addMinutes(startTime, defaultTime)
		if (
			this.slots()
				.map((slot) => slot.time)
				.some((time) => TimeUtils.isEqual(time, end))
		) {
			this.selectedTime.end = end
		}
	}
}
