import { Injectable, inject } from '@angular/core'
import { Router } from '@angular/router'
import { sortTablesByRoomPlan } from '@core/utils'
import {
	CreateTableCombinationRequest,
	DefaultService,
	SaveTableRequest,
	TableCombinationResponse,
	TableResponse,
	UserResponse,
} from '@api-client/angular'
import { HotToastService } from '@ngxpert/hot-toast'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import { ComponentStore } from '@ngrx/component-store'
import { finalize, take, zip } from 'rxjs'
import { I18nService } from '../../../core'
import {
	Coordinates,
	DEFAULT_COORDINATES,
	FuseMediaWatcherService,
	calculateRoomPlanCoordinates,
} from '../../../shared'

export enum SettingsView {
	TABLES,
	COMBINATIONS,
}

export enum CountOperation {
	INCREMENT,
	DECREMENT,
}

export interface RoomPlanSettingsState {
	tables: TableResponse[]
	combinations: TableCombinationResponse[]
	users: UserResponse[]
	selectedTable?: TableResponse
	selectedTableIndex?: number
	selectedCombinationIndex?: number
	loading: boolean
	isScreenSmall: boolean
	coordinates: Coordinates
	activeView: SettingsView
}

@UntilDestroy({ checkProperties: true })
@Injectable()
export class RoomPlanSettingsStore extends ComponentStore<RoomPlanSettingsState> {
	private readonly _router = inject(Router)
	private readonly _toast = inject(HotToastService)
	private readonly _i18nService = inject(I18nService)
	private readonly _apiService = inject(DefaultService)
	private readonly _mediaWatcherService = inject(FuseMediaWatcherService)

	constructor() {
		super({
			tables: [],
			combinations: [],
			users: [],
			selectedTable: undefined,
			selectedTableIndex: undefined,
			selectedCombinationIndex: undefined,
			loading: false,
			isScreenSmall: false,
			coordinates: DEFAULT_COORDINATES,
			activeView: SettingsView.TABLES,
		})

		this._mediaWatcherService.onMediaChange$.pipe(untilDestroyed(this)).subscribe(({ matchingAliases }) => {
			const isScreenSmall = !matchingAliases.includes('xl')
			this.patchState({ isScreenSmall })
		})

		this.loadData()
	}

	readonly tables$ = this.select((state) => state.tables)
	readonly combinations$ = this.select((state) => state.combinations)
	readonly users$ = this.select((state) => state.users)
	readonly selectedTable$ = this.select((state) => state.selectedTable)
	readonly selectedTableIndex$ = this.select((state) => state.selectedTableIndex)
	readonly selectedCombination$ = this.select((state) =>
		state.selectedCombinationIndex === undefined ? undefined : state.combinations[state.selectedCombinationIndex],
	)
	readonly selectedCombinationIndex$ = this.select((state) => state.selectedCombinationIndex)
	readonly loading$ = this.select((state) => state.loading)
	readonly isScreenSmall$ = this.select((state) => state.isScreenSmall)
	readonly coordinates$ = this.select((state) => state.coordinates)
	readonly activeView$ = this.select((state) => state.activeView)

	changeView(view: SettingsView): void {
		this.patchState({ activeView: view })
	}

	saveTables(): void {
		this.patchState({ loading: true })

		const tables = this.state().tables
		if (!tables.every((table) => table.name !== undefined && table.name.length > 0)) {
			this._toast.error(this._i18nService.translate('enter_table_names_error'))
			return
		}

		const combinations = this.state().combinations
		if (!combinations.every((combination) => combination.name !== undefined && combination.name.length > 0)) {
			this._toast.error(this._i18nService.translate('Please enter name of all combinations'))
			return
		}

		zip([
			this._apiService.roomPlanControllerSaveTables(tables),
			this._apiService.roomPlanControllerSaveTableCombinations(combinations),
		])
			.pipe(
				untilDestroyed(this),
				finalize(() => this.patchState({ loading: false })),
				this._toast.observe({
					loading: this._i18nService.translate('update_room_plan_loading'),
					success: this._i18nService.translate('update_room_plan_success'),
					error: this._i18nService.translate('update_room_plan_error'),
				}),
			)
			.subscribe()
	}

	selectTable(index: number): void {
		const table = this.state().tables[index]

		if (this.state().activeView === SettingsView.TABLES) {
			this.onSelectTable(table, index)
		} else {
			this.toggleCombinationTable(table.id, this.state().selectedCombinationIndex!)
		}
	}

	addTable(): void {
		this.tables$.pipe(take(1)).subscribe({
			next: (tables) => {
				const newTable: SaveTableRequest = {
					name: '',
					capacity: 2,
					columnStart: 0,
					rowStart: 0,
					width: 2,
					height: 2,
					shape: TableResponse.ShapeEnum.rectangle,
					priority: TableResponse.PriorityEnum.normal,
				}

				if (tables.length > 0) {
					const lastTable = sortTablesByRoomPlan(tables)[tables.length - 1]
					newTable.columnStart = lastTable.columnStart + lastTable.width + 1
					newTable.rowStart = lastTable.rowStart
				}

				tables.push(newTable as TableResponse)
				const coordinates = calculateRoomPlanCoordinates(tables)
				this.patchState({ tables, coordinates, selectedTable: newTable as TableResponse })
			},
		})
	}

	removeTable(index: number): void {
		this.tables$.pipe(take(1)).subscribe({
			next: (tables) => {
				tables.splice(index, 1)
				const coordinates = calculateRoomPlanCoordinates(tables)
				this.patchState({ tables, coordinates })
				this.onSelectTable(undefined, undefined)
			},
		})
	}

	increaseCapacity(index: number): void {
		this.tables$.pipe(take(1)).subscribe({
			next: (tables) => {
				tables[index].capacity += 1
				this.patchState({ tables })
			},
		})
	}

	decreaseCapacity(index: number): void {
		this.tables$.pipe(take(1)).subscribe({
			next: (tables) => {
				if (tables[index].capacity > 1) tables[index].capacity -= 1
				this.patchState({ tables })
			},
		})
	}

	updateTableSize(width: number, height: number, index: number): void {
		this.tables$.pipe(take(1)).subscribe({
			next: (tables) => {
				tables[index].width = width
				tables[index].height = height
				const coordinates = calculateRoomPlanCoordinates(tables)
				this.patchState({ tables, coordinates })
			},
		})
	}

	updateTableCoordinates(columnStart: number, rowStart: number, index: number): void {
		this.tables$.pipe(take(1)).subscribe({
			next: (tables) => {
				tables[index].columnStart = columnStart
				tables[index].rowStart = rowStart
				const coordinates = calculateRoomPlanCoordinates(tables)
				this.patchState({ tables, coordinates })
			},
		})
	}

	addCombination(): void {
		const combinations = this.state().combinations

		const combination: CreateTableCombinationRequest = {
			name: '',
			tableIds: [],
		}

		combinations.push(combination as TableCombinationResponse)

		this.patchState({ combinations, selectedCombinationIndex: combinations.length - 1 })
	}

	removeCombination(index: number): void {
		const combinations = this.state().combinations

		combinations.splice(index, 1)

		this.patchState({ combinations, selectedCombinationIndex: undefined })
	}

	toggleCombinationTable(tableId: string, combinationIndex: number): void {
		const combinations = this.state().combinations
		const index = combinations[combinationIndex].tableIds.indexOf(tableId)

		if (index === -1) {
			combinations[combinationIndex].tableIds.push(tableId)
		} else {
			combinations[combinationIndex].tableIds.splice(index, 1)
		}

		this.patchState({ combinations })
	}

	selectCombination(index: number): void {
		this.patchState({ selectedCombinationIndex: index })
	}

	private onSelectTable(table?: TableResponse, index?: number): void {
		this.isScreenSmall$.pipe(take(1)).subscribe({
			next: (isScreenSmall) => {
				this.patchState({ selectedTable: table, selectedTableIndex: index })
				const isDetailsPage = location.href.includes('details')
				if (table && !isDetailsPage) this._router.navigateByUrl('/settings/room-plan/details')
				else if (isScreenSmall && isDetailsPage) this._router.navigateByUrl('/settings/room-plan')
			},
		})
	}

	private loadData(): void {
		this._apiService
			.roomPlanControllerFindAllTables()
			.pipe(untilDestroyed(this))
			.subscribe({
				next: (tables) => {
					const coordinates = calculateRoomPlanCoordinates(tables)
					this.patchState({ tables, coordinates })
				},
			})

		this._apiService
			.roomPlanControllerFindAllTableCombinations()
			.pipe(untilDestroyed(this))
			.subscribe({
				next: (combinations) => {
					this.patchState({ combinations })
				},
			})

		this._apiService.userControllerFindAll().subscribe({
			next: (users) => this.patchState({ users }),
		})
	}
}
