import { Host, HostBinding, Inject, Output, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { ControlContainer, FormControl, FormGroupDirective, NgForm, NgModel } from '@angular/forms';
import { HypermediaField, HypermediaFieldOption, LabelingService } from 'first-npm-package-nicule/core';
import { TranslateService } from '@ngx-translate/core';
import { ErrorStateMatcher } from '@angular/material/core';

export class CustomErrorStateMatcher implements ErrorStateMatcher {
    isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
        const isSubmitted = form && form.submitted;
        const { invalid, dirty, touched } = control;

        return invalid && (dirty && isSubmitted || touched);
    }
}

export class BaseInputComponent {
    @Output() @ViewChild(NgModel, { static: true }) ngModel: NgModel;
    @Output() @ViewChildren(NgModel) ngModels: QueryList<NgModel>;

    field: HypermediaField;
    labelingService: LabelingService;

    uniqueText: string;

    matcher = new CustomErrorStateMatcher();

    private _settings: any = {} as any;
    get settings(): any {
        return this._settings;
    }
    set settings(newSettings: any) {
        const { errors, value } = newSettings;

        if (value) {
            this.ngModel.control.setValue(value);
        }

        if (errors) {
            const errobj = Object.keys(errors)
                .reduce((acc, key) => {
                    if (errors[key]) {
                        return { ...acc, [key]: errors[key] };
                    }

                    return acc;
                }, {});
            const errorCount = Object.keys(errobj).length;

            if (errorCount > 0) {
                this.ngModel.control.setErrors(errobj);
            } else {
                this.ngModel.control.setErrors(undefined);
            }
        }

        this._settings = newSettings;
    }

    @HostBinding('class')
    get mode(): 'regular' | 'narrow' | 'wide' {
        return this.settings.mode || 'regular';
    }

    get disabled(): boolean {
        return this.settings.disabled || false;
    }

    get options(): Array<HypermediaFieldOption> {
        return this.settings.options || this.field.options;
    }

    get interpolation(): boolean {
        return this.settings.interpolation || {};
    }

    get label(): string {
        const actionNameForTranslation  = this._settings.actionNameForTranslation ? this._settings.actionNameForTranslation : this.form.name;
        return this.labelingService.label(this.field.name, actionNameForTranslation, 'label');
    }

    get tooltip(): string {
        const tooltipKey = this.labelingService.label(this.field.name, this.form.name, 'tooltip');
        const translatedTooltip = this.translateService.instant(tooltipKey);

        return translatedTooltip === tooltipKey || translatedTooltip === 'tooltip' ? undefined : translatedTooltip;
    }

    get errors(): Array<{ name: string, message: string, payload: any }> {
        if (this.hasErrors) {
            const errors = this.ngModel.errors as { [key: string]: any };

            return Object
                .keys(errors)
                .reduce((acc, name) => {
                    const payload = typeof errors[name] === 'object' ? errors[name] : {};

                    return [...acc, { name, message: this.labelingService.label(this.field.name, this.form.name, `errors.${name}`), payload }];
                }, []);
        }

        return [];
    }

    get hasErrors(): boolean {
        const { invalid, dirty, touched } = this.ngModel.control;

        return invalid && (dirty || touched);
    }

    get placeholder(): string {
        const placeholderKey = this.labelingService.label(this.field.name, this.form.name, 'placeholder');
        const translatedPlaceholder = this.translateService.instant(placeholderKey, this.interpolation);

        return placeholderKey === translatedPlaceholder || translatedPlaceholder === 'placeholder' ? '' : translatedPlaceholder;
    }

    get uniqueIdentifier(): string {
        return this.uniqueText;
    }

    constructor(@Host() public form: ControlContainer,
                @Inject(TranslateService) public translateService: TranslateService) {

        this.uniqueText = Math.random()
            .toString(36)
            .substring(2, 4) + Math.random()
            .toString(36)
            .substring(2, 5);
    }

    error(key: string): string {
        return this.labelingService.label(this.field.name, this.form.name, `errors.${key}`);
    }

    onSettingsChanged(): void {
        if (this.settings.validators && this.settings.validators instanceof Array) {
            this.ngModel.control.setValidators(this.settings.validators);
        }
        if (this.settings.asyncValidators && this.settings.asyncValidators instanceof Array) {
            this.ngModel.control.setValidators(this.settings.asyncValidators);
        }
    }
}
