import { AbstractControl, AsyncValidatorFn, FormArray, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { CrmCustomerService } from '@domain/crm';
import { CountryDTO, CustomerDTO, InputOptionsDTO, KeyValueDTOOfStringAndString } from '@swagger/crm';
import { Observable, of } from 'rxjs';
import { catchError, debounceTime, distinctUntilChanged, first, map, switchMap } from 'rxjs/operators';
import { UiModalService } from '@ui/modal';
import { BreadcrumbService } from '@core/breadcrumb';
import { ApplicationService } from '@core/application';
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
import { DomainCheckoutService } from '@domain/checkout';
import { ChangeDetectorRef, ElementRef } from '@angular/core';
import { isBoolean } from '@utils/common';
import { setInvalidPropertyErrors } from '@ui/validators';
import { camelCase } from 'lodash';
import { validateEmail } from '../validators/email-validator';
import { HttpErrorResponse } from '@angular/common/http';

export abstract class CustomerCreateComponentBase {
  emailExistsValidator(): AsyncValidatorFn {
    return (control) =>
      control.valueChanges.pipe(
        debounceTime(500),
        distinctUntilChanged(),
        switchMap((value) => this.customerService.emailExists(value)),
        map((result) => {
          if (result?.result) {
            return { exists: result?.message ? result.message : 'E-Mail existiert bereits' };
          }
        }),
        catchError((error) => {
          if (error instanceof HttpErrorResponse) {
            if (error?.error?.invalidProperties?.email) {
              return of({ invalid: error.error.invalidProperties.email });
            } else {
              return of({ invalid: 'E-Mail ist ungültig' });
            }
          }
        }),
        first()
      );
  }

  type: string;
  control: FormGroup;

  countries$: Observable<CountryDTO[]>;

  abstract activatedRoute: ActivatedRoute;
  abstract router: Router;
  abstract modal: UiModalService;
  abstract customerService: CrmCustomerService;
  abstract breadcrumb: BreadcrumbService;
  abstract application: ApplicationService;
  abstract addressSelectionModal: AddressSelectionModalService;
  abstract checkoutService: DomainCheckoutService;
  abstract cdr: ChangeDetectorRef;

  cusotmers$: Observable<InputOptionsDTO>;
  private _p4mUser: KeyValueDTOOfStringAndString;
  private _p4mUserFeature: 'webshop' | 'store';

  get p4mUser() {
    return this._p4mUser;
  }

  set p4mUser(data: KeyValueDTOOfStringAndString) {
    this._p4mUser = data;
  }

  get p4mUserFeature() {
    return this._p4mUserFeature;
  }

  set p4mUserFeature(data: 'webshop' | 'store') {
    this._p4mUserFeature = data;
  }

  get organisationGroup(): AbstractControl {
    return this.control?.get('organisation');
  }

  init() {
    this.type = this.activatedRoute.snapshot?.routeConfig?.path;
    this.control = this.createControl(this.parseCustomerDataQuery(this.activatedRoute.snapshot.queryParams));

    this.countries$ = this.customerService.getCountries().pipe(map((response) => response.result));

    this.breadcrumb.addBreadcrumbIfNotExists({
      key: this.application.activatedProcessId,
      name: 'Kundendaten erfassen',
      path: `/kunde/${this.application.activatedProcessId}/customer/create/${this.type}`,
      tags: ['customer', 'create'],
      section: 'customer',
      params: {},
    });

    this.cusotmers$ = this.checkoutService
      .canSetCustomer({
        processId: this.application.activatedProcessId,
      })
      .pipe(
        map((r) => {
          const result = { ...r.create };
          result.options.values = [...result.options.values];
          result.options.values.forEach((f) => {
            f.selected = f.value === this.type;
            f.enabled = isBoolean(f.enabled) ? f.enabled : true;
          });

          // Upgrade p4mUser if necessary - Only show available option and select it
          if (!!this.p4mUser && !!r?.filter?.customertype) {
            if (!r.filter.customertype.includes(this.p4mUserFeature)) {
              result.options.values = result.options.values.filter(
                (option) => option.value === (this.p4mUserFeature === 'store' ? 'webshop' : 'store')
              );
              result.options.values[0].selected = true;
              result.options.values[0].enabled = true;
            }
          }
          return result.options;
        })
      );
  }

  async setType(options: InputOptionsDTO) {
    let selected = options.values.find((f) => f.selected);

    if (!selected) {
      selected = options.values.find((f) => f.enabled);
    }

    const navigated = await this.router.navigate(['../', selected.value], {
      relativeTo: this.activatedRoute,
      queryParams: this.createCustomerDataQuery(this.control.value),
    });

    // const navigated = await this.router.navigate(['../', type], {
    //   relativeTo: this.activatedRoute,
    //   queryParams: this.createCustomerDataQuery(this.control.value),
    // });
    // if (!navigated) {
    //   this.type = this.activatedRoute.snapshot?.routeConfig?.path;
    //   this.cdr.markForCheck();
    // }
  }

  createCustomerDataQuery(customer: CustomerDTO): { [key: string]: string } {
    const query: { [key: string]: string } = {
      gender: String(customer.gender),
      title: customer.title,
      firstName: customer.firstName,
      lastName: customer.lastName,
      email: customer.communicationDetails.email,
      phone: customer.communicationDetails.phone,
      mobile: customer.communicationDetails.mobile,
      street: customer.address.street,
      streetNumber: customer.address.streetNumber,
      zipCode: customer.address.zipCode,
      city: customer.address.city,
      country: customer.address.country,
      info: customer.address.info,
      name: customer.organisation.name,
      department: customer.organisation.department,
      vatId: customer.organisation.vatId,
      dateOfBirth: customer.dateOfBirth,
    };

    return query;
  }

  parseCustomerDataQuery(query: Params): CustomerDTO {
    if (Object.keys(query).length === 0) {
      return undefined;
    }

    if (query.card && query.cardFeature) {
      this.p4mUser = JSON.parse(decodeURIComponent(query.card));
      this.p4mUserFeature = decodeURIComponent(query.cardFeature) as 'webshop' | 'store';
    }

    return {
      gender: (+query.gender || 0) as any,
      title: query.title,
      firstName: query.firstName,
      lastName: query.lastName,
      communicationDetails: {
        email: query.email,
        phone: query.phone,
        mobile: query.mobile,
      },
      address: {
        street: query.street,
        streetNumber: query.streetNumber,
        zipCode: query.zipCode,
        city: query.city,
        country: query.country,
        info: query.info,
      },
      organisation: {
        name: query.name,
        department: query.department,
        vatId: query.vatId,
      },
      dateOfBirth: query.dateOfBirth,
    };
  }

  setShippingAddressValidators(group: AbstractControl) {
    group.get('gender')?.setValidators([Validators.required]);
    group.get('firstName')?.setValidators([Validators.required]);
    group.get('lastName')?.setValidators([Validators.required]);
    group.get('address')?.get('street')?.setValidators([Validators.required]);
    group.get('address')?.get('streetNumber')?.setValidators([Validators.required]);
    group.get('address')?.get('zipCode')?.setValidators([Validators.required]);
    group.get('address')?.get('city')?.setValidators([Validators.required]);
    group.get('address')?.get('country')?.setValidators([Validators.required]);
    group.get('communicationDetails')?.get('email')?.setValidators([validateEmail]);
  }

  clearShippingAddressValidators(group: AbstractControl) {
    group.get('gender')?.clearValidators();
    group.get('firstName')?.clearValidators();
    group.get('lastName')?.clearValidators();
    group.get('address')?.clearValidators();
    group.get('communicationDetails')?.clearValidators();
  }

  setValidationError(invalidProperties: { [key: string]: string }, formGroup: AbstractControl) {
    this.control.enable();
    this.control.reset(this.control.value);

    if (invalidProperties) {
      setInvalidPropertyErrors({ invalidProperties, formGroup });
    }

    this.control.markAllAsTouched();
  }

  setValidateAddressError(invalidProperties: { [key: string]: string }, control: AbstractControl) {
    const keys = Object.keys(invalidProperties);
    for (const key of keys) {
      control?.get(camelCase(key))?.setErrors({ validateAddress: invalidProperties[key] });
    }
  }

  focusFirstInvalidFormControl(formControlRef: ElementRef) {
    const invalidKeys = this.findInvalidControlsRecursive(this.control);
    const invalidControl = formControlRef.nativeElement.querySelector('[formcontrolname="' + invalidKeys?.find((_) => true) + '"]');
    invalidControl?.focus();
  }

  findInvalidControlsRecursive(formToInvestigate: FormGroup | FormArray): string[] {
    const invalidControls: string[] = [];
    let recursiveFunc = (form: FormGroup | FormArray) => {
      Object.keys(form.controls).forEach((field) => {
        if (field === 'shippingAddress') {
          return;
        }

        const control = form.get(field);
        if (control instanceof FormGroup) {
          recursiveFunc(control);
        } else if (control instanceof FormArray) {
          recursiveFunc(control);
        } else if (control.invalid) {
          invalidControls.push(field);
        }
      });
    };
    recursiveFunc(formToInvestigate);
    return invalidControls;
  }

  abstract createControl(customer?: CustomerDTO): FormGroup;

  abstract submit(): Promise<void>;

  async removeBreadcrumb() {
    const bc = await this.breadcrumb
      .getBreadcrumbsByKeyAndTags$(this.application.activatedProcessId, ['customer', 'create'])
      .pipe(first())
      .toPromise();

    if (bc.length > 0) {
      this.breadcrumb.removeBreadcrumb(bc[0].id);
    }
  }
}
