import { NgForOf, NgTemplateOutlet } from '@angular/common';
import {
    AfterViewInit,
    ChangeDetectorRef,
    Component,
    ElementRef,
    HostBinding,
    Input,
    OnChanges,
    SimpleChanges,
    TemplateRef,
    ViewChild,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MatChipListbox, MatChipSelectionChange, MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
import { StopPropagationDirective } from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';

@Component({
    selector: 'redaction-select',
    templateUrl: './select.component.html',
    styleUrls: ['./select.component.scss'],
    standalone: true,
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: SelectComponent,
            multi: true,
        },
    ],
    imports: [StopPropagationDirective, NgTemplateOutlet, TranslateModule, NgForOf, MatChipsModule, MatIconModule],
})
export class SelectComponent implements AfterViewInit, ControlValueAccessor, OnChanges {
    private _value: any[] = [];
    private _onChange: (value: any[]) => void;
    @Input() optionTemplate?: TemplateRef<{ option: any }>;
    @Input() label: string;
    @Input() options: any[];
    @Input() disabled = false;
    @Input() multiple = true;
    @ViewChild(MatChipListbox) chipList: MatChipListbox;

    constructor(private readonly _changeDetector: ChangeDetectorRef, private readonly _elementRef: ElementRef) {}

    private _height?: number;

    @Input()
    set height(value: number) {
        this._height = value;
        if (this.isFixedHeight) {
            const nativeElement = this._elementRef.nativeElement as HTMLElement;
            nativeElement.style.setProperty('--height', `${this._height}px`);
        }
    }

    @HostBinding('class.fixed-height')
    get isFixedHeight(): boolean {
        return !!this._height;
    }

    ngOnChanges(changes: SimpleChanges) {
        if (changes.options && this.chipList) {
            // This is completely shite, but we need to call _selectChips after the chips have been rendered
            setTimeout(() => {
                this._selectChips(this._value);
            });
        }
    }

    @Input() valueMapper: (option: any) => any = option => option.key;

    ngAfterViewInit(): void {
        setTimeout(() => {
            this._selectChips(this._value);
            this._changeDetector.markForCheck();
        });
    }

    selectChange($event: MatChipSelectionChange) {
        const chip = $event.source;
        if ($event.isUserInput) {
            if (chip.selected) {
                this._value = [...this._value, chip.value];
            } else {
                this._value = this._value.filter(o => o !== chip.value);
            }

            this._propagateChange([...new Set(this._value)]);
        }
    }

    registerOnChange(fn: any): void {
        this._onChange = fn;
    }

    // eslint-disable-next-line @typescript-eslint/no-empty-function
    registerOnTouched(): void {}

    writeValue(value: any[]): void {
        this._value = value ?? [];

        if (this.chipList) {
            this._selectChips(this._value);
        }
    }

    setDisabledState?(isDisabled: boolean): void {
        this.disabled = isDisabled;
    }

    selectAll() {
        this.chipList._chips.forEach(chip => {
            if (!chip.selected) {
                chip.toggleSelected(true);
            }
        });
    }

    deselectAll() {
        this.chipList._chips.forEach(chip => {
            if (chip.selected) {
                chip.toggleSelected(true);
            }
        });
    }

    private _selectChips(value: any[]): void {
        this.deselectAll();
        const chipsToSelect = this.chipList._chips.filter(c => value.includes(c.value));
        chipsToSelect.forEach(chip => chip.toggleSelected(true));
    }

    private _propagateChange(value: any[]): void {
        if (this._onChange) {
            this._onChange(value);
        }
    }
}
