import { Location } from '@angular/common';
import { HttpErrorResponse } from '@angular/common/http';
import { ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
import {
  AbstractControl,
  UntypedFormBuilder,
  UntypedFormGroup,
  ValidatorFn,
  Validators,
} from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { CrmCustomerService } from '@domain/crm';
import { CountryDTO, CustomerDTO, KeyValueDTOOfStringAndString } from '@generated/swagger/crm-api';
import { UiValidators } from '@ui/validators';
import { Observable, combineLatest } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
import { validateEmail } from '../../validators/email-validator';
import { requireGenderWhenNameIsSet } from '../../validators/gender-b2b-validator';
import { camelCase } from 'lodash';
import { CustomerSearchStore } from '../store';
import { CustomerSearchNavigation, NavigationRoute } from '@shared/services/navigation';
import { zipCodeValidator } from '../../validators/zip-code-validator';
import { GenderSettingsService } from '@shared/services/gender';

@Component({ template: '' })
export abstract class CustomerDataEditComponent implements OnInit {
  private customerService = inject(CrmCustomerService);
  private activatedRoute = inject(ActivatedRoute);
  private fb = inject(UntypedFormBuilder);
  private cdr = inject(ChangeDetectorRef);
  private location = inject(Location);
  private _store = inject(CustomerSearchStore);
  private _navigation = inject(CustomerSearchNavigation);
  public genderSettings = inject(GenderSettingsService);

  customer$: Observable<CustomerDTO>;
  customerId$: Observable<number>;
  countries$: Observable<CountryDTO[]>;
  customerFeatures$: Observable<KeyValueDTOOfStringAndString[]>;
  customerType$: Observable<string>;
  isWebshopOrGuest$: Observable<boolean>;
  customerFeatureB2bOrB2c$: Observable<string>;
  isOnlineOrCustomerCardUser$: Observable<boolean>;

  control: UntypedFormGroup;

  detailsRoute$: Observable<NavigationRoute>;

  get customerId() {
    return Number(this.activatedRoute.snapshot.params['customerId']);
  }

  afterInitForm?: (control: AbstractControl) => void;

  ngOnInit() {
    this.customerId$ = this.activatedRoute.params.pipe(map((p) => Number(p['customerId'])));
    this.customer$ = this.customerId$.pipe(
      switchMap((id) => this.customerService.getCustomer(id, 2)),
      map((cr) => cr.result),
    );
    this.customerFeatures$ = this.customer$.pipe(
      map((customer: CustomerDTO) => {
        // Bugfix - Logik muss aus folgendem Grund angepasst werden:
        // -> Bei B2B Kunden ist meist 'd-account' mit enabled: true versehen
        // -> Das feature mit dem key 'b2b' wird somit nicht zurückgegeben (da auf diesem Feature kein enabled vorhanden ist)
        // -> Die View im nachfolgenden Code wird jedoch anhand des keys 'b2b' aufgebaut
        const enabledFeatures = Object.values(customer.features).filter((f) => f.enabled);
        const b2bFeature = customer?.features?.find((f) => f?.key === 'b2b');
        const isB2B = enabledFeatures?.every(
          (f) =>
            f?.key === 'd-account' &&
            f?.description?.toLowerCase()?.includes('business') &&
            b2bFeature,
        );
        return isB2B ? [b2bFeature, ...enabledFeatures] : enabledFeatures;
      }),
    );

    this.customerType$ = this.customerFeatures$.pipe(
      map(
        (features: KeyValueDTOOfStringAndString[]) =>
          features.find(
            (f) => f.key === 'store' || f.key === 'webshop' || f.key === 'b2b' || f.key === 'guest',
          )?.key,
      ),
    );
    this.isOnlineOrCustomerCardUser$ = combineLatest([
      this.customerType$,
      this.customerFeatures$,
    ]).pipe(
      map(
        ([type, features]) =>
          type === 'webshop' ||
          !!features?.find((feature) => feature?.key === 'p4muser' || feature?.key === 'd-account'),
      ),
    );
    this.isWebshopOrGuest$ = this.customerType$.pipe(
      map((type) => type === 'webshop' || type === 'guest'),
    );
    this.customerFeatureB2bOrB2c$ = this.customerType$.pipe(
      map((type) => (type === 'b2b' ? 'b2b' : 'b2c')),
    );
    this.countries$ = this.isWebshopOrGuest$.pipe(
      switchMap((webshopOrGuest) => {
        if (!webshopOrGuest) {
          return this.customerService.getCountries().pipe(map((p) => p.result));
        } else {
          return this.customerService.getCountries().pipe(
            map((p) => p.result),
            map((countries) => countries.filter((country) => country.name === 'Deutschland')),
          );
        }
      }),
    );
    this.initForm();

    this.detailsRoute$ = combineLatest([this._store.processId$, this._store.customerId$]).pipe(
      map(([processId, customerId]) => this._navigation.detailsRoute({ processId, customerId })),
    );
  }

  async initForm() {
    const { fb } = this;
    const customerDTO = await this.customer$.pipe(first()).toPromise();
    const customerType = await this.customerType$.pipe(first()).toPromise();
    const customerFeatures = await this.customerFeatures$.pipe(first()).toPromise();
    const isB2b = customerType === 'b2b';
    const isWebshop = customerType === 'webshop';
    const isCard = customerFeatures.find((feature) => feature.key === 'p4muser');

    const zipCodeValidators =
      (isWebshop && isCard) || isWebshop || isB2b ? [Validators.required, zipCodeValidator()] : []; // #2573 Validierung nur für Onlinekonto, Onlinekonto+Kundenkarte, Business Konto

    const genderValidators: ValidatorFn[] = [];

    if (!isB2b) {
      genderValidators.push(Validators.required);
    }

    this.control = fb.group(
      {
        gender: fb.control(customerDTO?.gender, genderValidators),
        title: fb.control(customerDTO?.title),
        lastName: fb.control(customerDTO?.lastName, [Validators.required]),
        firstName: fb.control(customerDTO?.firstName, [Validators.required]),
        dateOfBirth: fb.control(customerDTO?.dateOfBirth, [
          isWebshop && isCard ? Validators.required : () => null,
          UiValidators.date,
        ]),
        communicationDetails: fb.group({
          email: fb.control(customerDTO?.communicationDetails?.email, [validateEmail]),
          phone: fb.control(customerDTO?.communicationDetails?.phone),
          mobile: fb.control(customerDTO?.communicationDetails?.mobile),
        }),
        organisation: fb.group({
          name: fb.control(customerDTO?.organisation?.name),
          vatId: fb.control(customerDTO?.organisation?.vatId),
          department: fb.control(customerDTO?.organisation?.department),
        }),
        address: fb.group({
          street: fb.control(customerDTO?.address?.street),
          streetNumber: fb.control(customerDTO?.address?.streetNumber),
          zipCode: fb.control(customerDTO?.address?.zipCode, zipCodeValidators),
          city: fb.control(customerDTO?.address?.city),
          country: fb.control(customerDTO?.address?.country),
          info: fb.control(customerDTO?.address?.info),
        }),
      },
      {
        // #1461 KKM/ISA: Anrede als Pflichtfeld
        validators: [requireGenderWhenNameIsSet],
      },
    );

    if (typeof this.afterInitForm === 'function') {
      this.afterInitForm.call(this, this.control);
    }

    this.control.markAllAsTouched();
    this.cdr.markForCheck();
  }

  cancel() {
    this.location.back();
  }

  async submit() {
    if (!this.control.valid || !this.control.enabled) {
      return;
    }

    this.control.disable();

    try {
      await this.customerService.patchCustomer(this.customerId, this.control.value).toPromise();
      this._store.selectCustomer({ customerId: this._store.customerId, reload: true });

      this.location.back();
    } catch (error) {
      this.control.enable();
      if (error instanceof HttpErrorResponse) {
        if (error.error.invalidProperties) {
          const invProps = error.error.invalidProperties;
          const keys = Object.keys(invProps);
          for (const key of keys) {
            this.control
              .get('address')
              ?.get(camelCase(key))
              ?.setErrors({ validateAddress: invProps[key] });
          }
        }
      }
      console.error(error);
    }
  }
}
