import { Component, computed, effect, inject, viewChild } from '@angular/core'
import { NonNullableFormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'
import {
	CategoryResponse,
	CreateMenuItemRequest,
	DefaultService,
	MenuItemCategoryRequest,
	MenuItemResponse,
	UpdateMenuItemRequest,
} from '@api-client/angular'
import { CheckboxModule, LoadingButtonDirective } from '@frontend/shared'
import { TranslocoDirective } from '@jsverse/transloco'
import { NgSelectComponent } from '@ng-select/ng-select'
import { HotToastService } from '@ngxpert/hot-toast'
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'
import { intersection } from 'es-toolkit/compat'
import { finalize, firstValueFrom, Observable, tap } from 'rxjs'
import { MenuSettingsStore } from '../+state'
import { ALLERGENS_LIST, I18nService } from '../../../../core'
import { extractHttpErrorMessage, ImageUploadComponent } from '../../../../shared'
import { ImageUploadComponent as ImageUploadComponent_1 } from '../../../../shared/components/image-upload/image-upload.component'
import { toSignal } from '@angular/core/rxjs-interop'
import { LucideArchive, LucideChevronDown, LucideRotateCcw, LucideTrash, LucideX } from '@lucide/angular'

@UntilDestroy({ checkProperties: true })
@Component({
	selector: 'kadi-add-edit-item',
	templateUrl: './add-edit-item.component.html',
	styleUrls: ['./add-edit-item.component.scss'],
	imports: [
		TranslocoDirective,
		ReactiveFormsModule,
		ImageUploadComponent_1,
		NgSelectComponent,
		CheckboxModule,
		LoadingButtonDirective,
		LucideX,
		LucideChevronDown,
		LucideRotateCcw,
		LucideArchive,
		LucideTrash,
	],
})
export class AddEditItemComponent {
	private readonly _toast = inject(HotToastService)
	private readonly _store = inject(MenuSettingsStore)
	private readonly _i18nService = inject(I18nService)
	private readonly _formBuilder = inject(NonNullableFormBuilder)
	private readonly _apiService = inject(DefaultService)

	readonly uploadComponent = viewChild.required(ImageUploadComponent)

	categories = this._store.categories
	rootCategories = computed(() => this.categories().filter((category) => !category.parentCategoryId))
	items = this._store.menuItems
	item!: MenuItemResponse

	selectedCategories: string[] = []
	subcategories: CategoryResponse[] = []
	selectedSubcategories: string[] = []

	unitsMap: { name: string; value: string }[] = []
	currencyList = toSignal(this._apiService.staticControllerGetCurrencies())
	showAllergens = false
	allergensList = ALLERGENS_LIST
	loading = false

	form = this._formBuilder.group({
		name: this._formBuilder.control('', {
			validators: Validators.compose([Validators.required, Validators.minLength(2), Validators.maxLength(50)]),
		}),
		description: this._formBuilder.control<string | undefined>(undefined, { validators: Validators.maxLength(250) }),
		price: this._formBuilder.control(1, {
			validators: Validators.compose([Validators.required, Validators.min(0.01)]),
		}),
		currency: this._formBuilder.control('EUR', { validators: Validators.required }),
		amount: this._formBuilder.control(1, {
			validators: Validators.compose([Validators.required, Validators.min(0.000_001)]),
		}),
		unit: this._formBuilder.control<CreateMenuItemRequest.UnitEnum>(CreateMenuItemRequest.UnitEnum.pcs, {
			validators: Validators.required,
		}),
		allergens: this._formBuilder.control<string[]>([]),
		coverUrl: this._formBuilder.control<string | undefined>(undefined),
		categories: this._formBuilder.control<string[]>([], { validators: Validators.required }),
		subcategories: this._formBuilder.control<string[] | undefined>([]),
	})

	constructor() {
		this.mapEnums()

		effect(() => {
			const item = this._store.selectedItem()
			if (!item) return

			this.uploadComponent()?.reset()

			this.item = item
			this.initForm(item)

			const selectedCategories = this.categories().filter((category) =>
				item.categories.map((x) => x.categoryId).includes(category.id as string),
			)
			this.filterSubcategories(selectedCategories)
		})
	}

	get f() {
		return this.form.controls
	}

	filterSubcategories(selectedCategories: CategoryResponse[]): void {
		const categories = this.categories()

		const subCategories: CategoryResponse[] = []
		for (const category of selectedCategories) {
			subCategories.push(...categories.filter((_category) => _category.parentCategoryId === category.id))
		}
		this.subcategories = subCategories

		// Update form value by only applying the subcategories that exist in current selection
		this.f.subcategories.setValue(
			intersection(
				this.subcategories.map((category) => category.id),
				this.f.subcategories.value,
			),
		)
	}

	close(): void {
		this.form.reset()
		this._store.selectItem(undefined)
	}

	async save(): Promise<void> {
		this.loading = true
		const categories: MenuItemCategoryRequest[] = []
		const subcategories: MenuItemCategoryRequest[] = []

		const items = this.items()

		// eslint-disable-next-line unicorn-x/no-array-for-each
		this.form.value.categories?.forEach((categoryId: string) => {
			const existing = this.item.categories.find((_category) => _category.categoryId === categoryId)
			if (existing) categories.push(existing)
			else {
				categories.push({
					categoryId,
					sortOrder:
						items.filter((_item) => _item.categories.map((category) => category.categoryId).includes(categoryId))
							.length + 1,
				})
			}
		})

		// eslint-disable-next-line unicorn-x/no-array-for-each
		this.form.value.subcategories?.forEach((categoryId: string) => {
			const existing = this.item.categories.find((_category) => _category.categoryId === categoryId)
			if (existing) subcategories.push(existing)
			else {
				subcategories.push({
					categoryId,
					sortOrder:
						items.filter((item) => item.categories.map((category) => category.categoryId).includes(categoryId)).length +
						1,
				})
			}
		})

		await firstValueFrom(
			this.uploadComponent()
				.upload()
				.pipe(
					tap((url) => {
						if (url) this.f.coverUrl.setValue(url)
					}),
				),
		)

		const formValue = this.form.getRawValue()

		let call$: Observable<void>
		if (this.item.id) {
			const request: UpdateMenuItemRequest = {
				...formValue,
				id: this.item.id,
				categories,
				subcategories,
			}
			call$ = this._store.updateItem(this.item.id, request)
		} else {
			const request: CreateMenuItemRequest = {
				...formValue,
				categories,
				subcategories,
			}
			call$ = this._store.saveItem(request)
		}

		call$
			.pipe(
				untilDestroyed(this),
				finalize(() => (this.loading = false)),
				this._toast.observe({
					success: this._i18nService.translate(this.item.id ? 'item_update_success' : 'item_add_success', {
						itemName: formValue.name,
					}),
					error: (error) => extractHttpErrorMessage(error),
				}),
			)
			.subscribe({
				next: () => {
					this.form.markAsPristine()
				},
			})
	}

	toggleArchive(): void {
		this._store.toggleItemArchive(this.item)
	}

	delete(): void {
		this._store.deleteItem(this.item)
	}

	private mapEnums(): void {
		this.unitsMap = Object.keys(CreateMenuItemRequest.UnitEnum)
			.filter((type) => Number.isNaN(type))
			.map((x) => ({ name: x, value: CreateMenuItemRequest.UnitEnum[x as never] }))
	}

	private initForm(item: MenuItemResponse): void {
		const selectedCategories = item.categories.map((_category) => _category.categoryId)
		const selectedSubcategories = item.subcategories?.map((_category) => _category.categoryId)

		this.form.patchValue({
			...item,
			categories: selectedCategories,
			subcategories: selectedSubcategories,
		})
	}
}
