import { ButtonSize, ButtonType } from '@af/digi-ng/_button/button';
import { FormSelectItem } from '@af/digi-ng/_form/form-select';
import {
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnChanges,
  OnInit,
  Output,
  SimpleChanges,
  ViewChild,
} from '@angular/core';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { RoleEnum } from '@msfa-enums/role.enum';
import { EmployeeEditRequest } from '@msfa-models/api/employee-edit.request.model';
import { Employee } from '@msfa-models/employee.model';
import { CustomError } from '@msfa-models/error/custom-error';
import { Role } from '@msfa-models/role.model';
import { Tjanst } from '@msfa-models/tjanst.model';
import { UtforandeVerksamhet } from '@msfa-models/utforande-verksamhet.model';
import { UtforandeVerksamheterService } from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service';
import { ValidationErrorLink } from '@msfa-shared/components/error-list/error-list.component';
import { TreeNodesSelectorService } from '@msfa-shared/components/tree-nodes-selector/services/tree-nodes-selector.service';
import { uuid } from '@msfa-utils/uuid';
import { EmployeeValidator } from '@msfa-utils/validators/employee.validator';
import { EmployeeFormService } from '../services/employee-form.service';

@Component({
  selector: 'msfa-edit-employee-form',
  templateUrl: './edit-employee-form.component.html',
  styleUrls: ['./edit-employee-form.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class EditEmployeeFormComponent implements OnInit, OnChanges {
  @ViewChild('editEmployeeFormContainer') editEmployeeFormContainer: ElementRef;

  @Input() employee: Employee;
  @Input() availableRoles: Role[];
  @Input() availableTjanster: Tjanst[];
  @Input() isLoadingUtforandeVerksamheter = false;
  @Input() availableUtforandeVerksamheter: UtforandeVerksamhet[];
  @Input() errorWhileUpdating: CustomError;

  @Output() tjansterSelected = new EventEmitter<Tjanst[]>();
  @Output() closeError = new EventEmitter<void>();
  @Output() formSubmitted = new EventEmitter<EmployeeEditRequest>();

  readonly ButtonType = ButtonType;
  readonly ButtonSize = ButtonSize;

  readonly emailFormControlName = 'email';
  readonly tjansterFormControlName = 'tjanster';
  readonly utforandeVerksamheterFormControlName = 'utforandeVerksamheter';
  readonly selectAllUtforandeVerksamheterFormControlName = 'allaUtforandeVerksamheter';
  readonly utforandeVerksamhetRequiredMessage = 'Välj minst en utförande verksamhet';
  readonly formUuid = uuid();
  readonly emailElementId = `email-control-${this.formUuid}`;
  readonly tjansterElementId = `tjanster-control-${this.formUuid}`;
  readonly selectAllUtforandeVerksamheterElementId = `select-all-utforande-verksamheter-control-${this.formUuid}`;
  readonly utforandeVerksamhetElementId = `utforande-verksamhet-control-${this.formUuid}`;
  readonly firstValidationErrorLinkId = `validation-error-link-${this.formUuid}`;

  private _submitted = false;

  editEmployeeFormGroup: FormGroup | null = null;
  displayEditWithoutRolesDialog = false;

  selectableTjansterFormItems: Array<FormSelectItem> | null = null;

  constructor(
    private employeeFormService: EmployeeFormService,
    private utforandeVerksamheterService: UtforandeVerksamheterService,
    private treeNodesSelectorService: TreeNodesSelectorService
  ) {
    this.updateSelectableTjansterFormItems();
  }

  ngOnInit(): void {
    this.initializeEditEmployeeFormGroup();
    this.toggleTjanst();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.availableRoles) {
      this.initializeEditEmployeeFormGroup();
    }

    if (changes.availableTjanster) {
      this.updateSelectableTjansterFormItems();
    }

    if (changes.availableUtforandeVerksamheter) {
      this.editEmployeeFormGroup.patchValue(
        Object.fromEntries([
          [
            this.utforandeVerksamheterFormControlName,
            this.utforandeVerksamheterService.getTreeNodeDataFromUtforandeVerksamheter(
              this.availableUtforandeVerksamheter,
              this.employee?.utforandeVerksamheter
            ),
          ],
        ])
      );

      this.updateUtforandeVerksamhetStatus();
    }
  }

  get emailFormControl(): AbstractControl | undefined {
    return this.editEmployeeFormGroup?.get(this.emailFormControlName);
  }

  get tjansterFormControl(): AbstractControl | undefined {
    return this.editEmployeeFormGroup?.get(this.tjansterFormControlName);
  }

  get rolesFormGroup(): AbstractControl {
    return this.editEmployeeFormGroup.get('roles');
  }

  get utforandeVerksamheterFormControl(): AbstractControl | undefined {
    return this.editEmployeeFormGroup?.get(this.utforandeVerksamheterFormControlName);
  }

  get selectAllUtforandeVerksamheterFormControl(): AbstractControl | undefined {
    return this.editEmployeeFormGroup?.get(this.selectAllUtforandeVerksamheterFormControlName);
  }

  get displayPristineWarning(): boolean {
    return this._submitted && this.editEmployeeFormGroup.pristine && this.editEmployeeFormGroup.valid;
  }
  get displayErrorSummary(): boolean {
    return this._submitted && this.editEmployeeFormGroup.invalid;
  }

  private updateUtforandeVerksamhetStatus(): void {
    if (this.availableUtforandeVerksamheter && this.availableUtforandeVerksamheter.length > 0) {
      this.utforandeVerksamheterFormControl.enable();
      this.selectAllUtforandeVerksamheterFormControl.enable();
      return;
    }

    this.utforandeVerksamheterFormControl.disable();
    this.selectAllUtforandeVerksamheterFormControl.disable();
  }

  private updateSelectableTjansterFormItems(): void {
    this.selectableTjansterFormItems = this.availableTjanster?.map(tjanst => {
      return { name: tjanst.name, value: `${tjanst.tjanstId}` };
    });
  }

  private initializeEditEmployeeFormGroup(): void {
    const currentTjanst = this.employee.tjanster[0];
    const tjanstId = currentTjanst
      ? this.availableTjanster.find(tjanst => tjanst.code === currentTjanst.tjansteKod).tjanstId
      : null;

    this.editEmployeeFormGroup = new FormGroup(
      {
        email: new FormControl(this.employee.email),
        tjanster: new FormControl(tjanstId),
        roles: this.employeeFormService.getRolesFormGroup(this.availableRoles, this.employee.roles),
        utforandeVerksamheter: new FormControl(
          this.utforandeVerksamheterService.getTreeNodeDataFromUtforandeVerksamheter(
            this.availableUtforandeVerksamheter,
            this.employee?.utforandeVerksamheter
          )
        ),
        allaUtforandeVerksamheter: new FormControl(this.employee.allaUtforandeVerksamheter),
      },
      {
        validators: [
          EmployeeValidator.isEmployeeValid(this.utforandeVerksamheterService.hasSelectedUtforandeVerksamhet),
        ],
      }
    );

    this.updateUtforandeVerksamhetStatus();
  }

  getFormControlName(role: Role): string {
    return this.employeeFormService.getFormControlName(role);
  }

  abortFormSubmit(): void {
    this.displayEditWithoutRolesDialog = false;
  }

  onFormSubmitted(saveWithoutRoles = false): void {
    if (!this.editEmployeeFormGroup || this.isLoadingUtforandeVerksamheter) {
      return;
    }

    const roles = this.employeeFormService.getRolesFromFormGroup(this.rolesFormGroup, this.availableRoles);

    if (!roles.length && !saveWithoutRoles) {
      this.displayEditWithoutRolesDialog = true;
      return;
    }

    this._submitted = true;

    this.editEmployeeFormGroup.markAllAsTouched();

    if (this.editEmployeeFormGroup.invalid) {
      setTimeout(() => {
        this.focusElement('.error-list__validation-error-link a');
      });

      return;
    }

    if (this.editEmployeeFormGroup.pristine) {
      return;
    }

    this.formSubmitted.emit({
      email: this.emailFormControl?.value as string,
      tjanstIds: !roles.length
        ? []
        : this.employeeFormService
            .getSelectedTjanster(this.availableTjanster, +this.tjansterFormControl?.value)
            .map(tjanst => tjanst.tjanstId),
      roles: [...new Set([...roles, RoleEnum.MSFA_Standard])],
      adressIds:
        !roles.length || this.selectAllUtforandeVerksamheterFormControl.value
          ? []
          : this.utforandeVerksamheterService.getSelectedAdressIdsFromTreeNode(
              this.utforandeVerksamheterFormControl?.value
            ),
      allaUtforandeVerksamheter: !roles.length ? false : !!this.selectAllUtforandeVerksamheterFormControl.value,
    });
  }

  getValidationErrorLinks(): ValidationErrorLink[] {
    let validationErrorLinks: ValidationErrorLink[] = [];

    if (!this.editEmployeeFormGroup) {
      return;
    }
    const { email, tjanster, utforandeVerksamheter } = this.editEmployeeFormGroup.errors || {};

    if (email) {
      validationErrorLinks = validationErrorLinks.concat({
        elementId: this.emailElementId,
        text: email as string,
      });
    }

    if (tjanster) {
      validationErrorLinks = validationErrorLinks.concat({
        elementId: this.tjansterElementId,
        text: tjanster as string,
      });
    }

    if (utforandeVerksamheter) {
      validationErrorLinks = validationErrorLinks.concat({
        elementId: this.utforandeVerksamhetElementId,
        text: utforandeVerksamheter as string,
      });
    }

    return validationErrorLinks;
  }

  focusElement(selector: string): void {
    let errorListElement: HTMLElement = null;
    let linkElement: HTMLLinkElement = null;

    if (!this.editEmployeeFormContainer || !this.editEmployeeFormContainer.nativeElement) {
      return;
    }

    errorListElement = this.editEmployeeFormContainer.nativeElement as HTMLElement;
    linkElement = errorListElement?.querySelector(selector);
    linkElement?.focus();
  }

  toggleTjanst(): void {
    if (!this.tjansterFormControl.value) {
      return;
    }

    this.tjansterSelected.emit(
      this.employeeFormService.getSelectedTjanster(this.availableTjanster, +this.tjansterFormControl.value)
    );
  }

  updateToggleAllUtforandeVerksamheter(): void {
    const hasSelectedAllLeafNodes = this.treeNodesSelectorService.hasSelectedAllLeafNodes(
      this.utforandeVerksamheterFormControl.value
    );

    if (hasSelectedAllLeafNodes === this.selectAllUtforandeVerksamheterFormControl.value) {
      return;
    }

    this.editEmployeeFormGroup.patchValue(
      Object.fromEntries([[this.selectAllUtforandeVerksamheterFormControlName, hasSelectedAllLeafNodes]])
    );

    if (!hasSelectedAllLeafNodes) {
      return;
    }

    setTimeout(() => {
      this.focusElement(`#${this.selectAllUtforandeVerksamheterElementId}`);
    });
  }

  emitCloseError(): void {
    this.closeError.emit();
  }
}
