import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { Dictionary, IDictionary } from '@red/domain';
import { FormGroup, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { map, startWith } from 'rxjs/operators';
import { firstValueFrom, Observable } from 'rxjs';
import { toSnakeCase } from '@utils/functions';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { PermissionsService } from '@services/permissions.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';

const REDACTION_FIELDS = ['defaultReason'];

interface Color {
    label: string;
    placeholder: string;
    controlName: string;
    hasColor$: Observable<boolean>;
    labelParams: () => Record<string, string>;
}

@Component({
    selector: 'redaction-add-edit-entity [entity] [dossierTemplateId] [readOnly]',
    templateUrl: './add-edit-entity.component.html',
    styleUrls: ['./add-edit-entity.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddEditEntityComponent extends BaseFormComponent implements OnInit {
    @Input() dossierTemplateId: string;
    @Input() entity: Dictionary;
    @Input() readOnly: boolean;
    @Input() dossierId?: string;

    technicalName$: Observable<string>;

    colors: Color[];

    constructor(
        private readonly _dictionariesMapService: DictionariesMapService,
        private readonly _permissionsService: PermissionsService,
        private readonly _dictionaryService: DictionaryService,
        private readonly _toaster: Toaster,
        private readonly _loadingService: LoadingService,
        private readonly _formBuilder: UntypedFormBuilder,
        private readonly _changeRef: ChangeDetectorRef,
    ) {
        super();
    }

    cpDisabled(color: Color) {
        return this.form.get(color.controlName).disabled;
    }

    get #isDossierRedaction(): boolean {
        return this.entity?.type === 'dossier_redaction';
    }

    get #isSystemManaged(): boolean {
        return !!this.entity?.systemManaged;
    }

    get #addToDictionaryActionControl() {
        return { value: !!this.entity?.addToDictionaryAction, disabled: this.#isSystemManaged && !this.#isDossierRedaction };
    }

    get #isHint(): boolean {
        return !!this.form.get('hint')?.value;
    }

    revert(): void {
        this.form.patchValue(this.initialFormValue);
    }

    ngOnInit() {
        this._initializeForm();
    }

    async save(): Promise<void> {
        this._loadingService.start();
        const dictionary = this._formToObject();

        try {
            if (this.entity) {
                // edit mode
                await firstValueFrom(
                    this._dictionaryService.updateDictionary(dictionary, this.dossierTemplateId, dictionary.type, this.dossierId),
                );
                this._toaster.success(_('add-edit-entity.success.edit'));
            } else {
                // create mode
                await firstValueFrom(this._dictionaryService.addDictionary({ ...dictionary, dossierTemplateId: this.dossierTemplateId }));
                this._toaster.success(_('add-edit-entity.success.create'));
            }
            this.initialFormValue = this.form.getRawValue();
            this._changeRef.markForCheck();
            this._loadingService.stop();
        } catch (e) {
            this._loadingService.stop();
            throw e;
        }
    }

    #initializeColors(form: FormGroup): void {
        this.colors = [
            {
                label: _('add-edit-entity.form.color'),
                labelParams: () => ({ type: this.#isHint ? 'hint' : 'redaction' }),
                placeholder: _('add-edit-entity.form.color-placeholder'),
                controlName: 'hexColor',
                hasColor$: this._colorEmpty$(form, 'hexColor'),
            },
            {
                label: _('add-edit-entity.form.color'),
                labelParams: () => ({ type: 'recommendation' }),
                placeholder: _('add-edit-entity.form.color-placeholder'),
                controlName: 'recommendationHexColor',
                hasColor$: this._colorEmpty$(form, 'recommendationHexColor'),
            },
            {
                label: _('add-edit-entity.form.color'),
                labelParams: () => ({ type: this.#isHint ? 'ignored' : 'skipped' }),
                placeholder: _('add-edit-entity.form.color-placeholder'),
                controlName: 'skippedHexColor',
                hasColor$: this._colorEmpty$(form, 'skippedHexColor'),
            },
        ];
    }

    private _initializeForm(): void {
        let controlsConfig: Record<string, any> = {
            label: [this.entity?.label, [Validators.required, Validators.minLength(3)]],
            hexColor: [this.entity?.hexColor, [Validators.required, Validators.minLength(7)]],
            recommendationHexColor: [this.entity?.recommendationHexColor, [Validators.required, Validators.minLength(7)]],
            skippedHexColor: [this.entity?.skippedHexColor, [Validators.required, Validators.minLength(7)]],
        };

        if (!this.#isDossierRedaction) {
            controlsConfig = {
                ...controlsConfig,
                type: [this.entity?.type],
                description: [this.entity?.description],
                rank: [{ value: this.entity?.rank, disabled: this.#isSystemManaged }, Validators.required],
                hint: [{ value: !!this.entity?.hint, disabled: this.#isSystemManaged }],
                hasDictionary: [{ value: !!this.entity?.hasDictionary, disabled: this.#isSystemManaged }],
                caseSensitive: [{ value: this.entity ? !this.entity.caseInsensitive : false, disabled: this.#isSystemManaged }],
            };
        }

        if (!this.entity?.hint && !this.#isDossierRedaction) {
            Object.assign(controlsConfig, {
                defaultReason: [{ value: null, disabled: true }],
            });
        }

        if (this.entity?.hasDictionary || this.#isDossierRedaction) {
            Object.assign(controlsConfig, {
                addToDictionaryAction: [this.#addToDictionaryActionControl],
            });
        }

        const form = this._formBuilder.group(controlsConfig);

        this.#initializeColors(form);
        this.technicalName$ = form.get('label').valueChanges.pipe(map((value: string) => this._toTechnicalName(value)));

        if (!this.#isDossierRedaction) {
            form.get('hint').valueChanges.subscribe(isHint => {
                if (isHint) {
                    REDACTION_FIELDS.forEach(field => form.removeControl(field));
                } else {
                    form.addControl('defaultReason', new UntypedFormControl({ value: null, disabled: true }));
                }
            });

            form.get('hasDictionary').valueChanges.subscribe(hasDictionary => {
                if (hasDictionary) {
                    form.addControl('addToDictionaryAction', new UntypedFormControl(this.#addToDictionaryActionControl));
                } else {
                    form.removeControl('addToDictionaryAction');
                }
            });

            if (!this.entity) {
                form.get('label').valueChanges.subscribe((label: string) => {
                    form.get('type').setValue(this._toTechnicalName(label));
                });
            }
        }

        if (this.readOnly) {
            form.disable();
        }

        this.initialFormValue = form.getRawValue();

        this.form = form;
    }

    private _toTechnicalName(value: string) {
        const existingTechnicalNames = this._dictionariesMapService.get(this.dossierTemplateId).map(dict => dict.type);
        const baseTechnicalName = toSnakeCase(value.trim());
        let technicalName = baseTechnicalName.replaceAll(/[^A-Za-z0-9_-]/g, '');
        if (!technicalName.length && baseTechnicalName.length) {
            technicalName = '_';
        }
        let suffix = 1;
        while (existingTechnicalNames.includes(technicalName)) {
            technicalName = technicalName === '_' ? `${technicalName}${suffix++}` : [technicalName, suffix++].join('_');
        }
        return technicalName;
    }

    private _colorEmpty$(form: UntypedFormGroup, field: string) {
        return form.get(field).valueChanges.pipe(
            startWith(form.get(field).value),
            map((value: string) => !value || value?.length === 0),
        );
    }

    private _formToObject(): IDictionary {
        // Fields which aren't set for hints, need additional check
        const addToDictionaryAction = !!this.form.get('addToDictionaryAction')?.value;
        const hasDictionary = !!this.form.get('hasDictionary')?.value;

        let entity = {
            ...this.entity,
            label: this.form.get('label').value,
            hexColor: this.form.get('hexColor').value,
            recommendationHexColor: this.form.get('recommendationHexColor').value,
            skippedHexColor: this.form.get('skippedHexColor').value,
            dossierTemplateId: this.dossierTemplateId,
            addToDictionaryAction,
        };

        if (this.entity?.type !== 'dossier_redaction') {
            entity = {
                ...entity,
                type: this.form.get('type').value,
                description: this.form.get('description').value,
                hint: this.#isHint,
                rank: this.form.get('rank').value,
                caseInsensitive: !this.form.get('caseSensitive').value,
                hasDictionary,
            };
        }

        return entity;
    }
}
