import { Store, Select } from '@ngxs/store';
import { Component, OnInit, OnDestroy, Input, ViewChild, ChangeDetectorRef } from '@angular/core';
import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Subject, Observable, of } from 'rxjs';
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
import { EditCustomerData } from '../../../../core/models/edit-customer.model';
import { takeUntil, distinctUntilChanged, take, map, filter, switchMap } from 'rxjs/operators';
import { User, Address } from '../../../../core/models/user.model';
import { ChangeCurrentRoute } from 'apps/sales/src/app/core/store/actions/process.actions';
import { AddNewShippingAddress, AddNewInvoiceAddress } from 'apps/sales/src/app/core/store/actions/customer.actions';
import { GENDERS, TIITLES } from '../../dropdown-values';
import { CountrySelector } from 'apps/sales/src/app/core/store/selectors/countries.selector';
import { Country } from 'apps/sales/src/app/core/models/country.model';
import { isNullOrUndefined } from 'util';
import { scrollToFirstInvalidElement } from 'apps/sales/src/app/core/utils/app.utils';
import { AddressSugestionsComponent } from '../address-sugestions/address-sugestions.component';
import { AddressDTO } from '@swagger/crm';
import { CustomerService } from 'apps/sales/src/app/core/services/customer.service';
import { ErrorService } from 'apps/sales/src/app/core/error/component/error.service';
import { ButtonComponent } from '@libs/ui';
import { CustomValidators } from '../../../../shared/validation/custom-validation';
import { SaveFormState } from 'apps/sales/src/app/core/store/actions/forms.actions';
import { FormsSelectors } from 'apps/sales/src/app/core/store/selectors/forms.selectors';

export const ADDRESS_FORM_STATE_KEY = 'customer-address';

@Component({
  selector: 'app-customer-address-form',
  templateUrl: './customer-address-form.component.html',
  styleUrls: ['./customer-address-form.component.scss'],
})
export class CustomerAddressFormComponent implements OnInit, OnDestroy {
  @Input() customerInput: User;
  @Input() title: string;
  @Input() addressId: string;
  @Input() isBillingForm = true;
  @Select(SharedSelectors.getCustomerEditData) customerEditData$: Observable<EditCustomerData>;
  @Select(CountrySelector.getCountriesIterable) countries$: Observable<Country[]>;
  @ViewChild('suggestions')
  suggestions: AddressSugestionsComponent;
  @ViewChild('postBtn') postBtn: ButtonComponent;
  form: FormGroup;
  submitted = false;
  destroy$ = new Subject();
  customer: User;
  selecetedAddress: Address;
  addressSuggestions: Address[];

  genders = GENDERS;
  titels = TIITLES;
  showCompanyFields = false;
  countryList: Country[];
  cityGeneralError = false;
  addressGeneralError = false;
  addressNoGeneralError = false;
  zipGeneralError = false;
  selectedTitle: string;
  backendValidation = false;
  notEditable = false;

  get countries() {
    return this.countries$.pipe(
      map((t) => {
        return t.map((i) => i.value);
      })
    );
  }

  constructor(
    private fb: FormBuilder,
    private store: Store,
    private router: Router,
    private customerService: CustomerService,
    private cdrf: ChangeDetectorRef,
    private errorService: ErrorService,
    private route: ActivatedRoute
  ) {}

  ngOnInit(): void {
    if (this.customerInput) {
      // Edit
      this.customer = this.customerInput;

      if (this.isBillingForm) {
        this.selecetedAddress = this.customerInput.poossible_invoice_addresses.find((address: Address) => address.id === +this.addressId);
      } else {
        this.selecetedAddress = this.customerInput.poossible_delivery_addresses.find((address: Address) => address.id === +this.addressId);
      }
      this.buildForm(this.fb, this.selecetedAddress);
      this.onFormChange();
    } else {
      // Add
      this.customerEditData$
        .pipe(
          filter((data) => !isNullOrUndefined(data)),
          take(1)
        )
        .subscribe((data: EditCustomerData) => {
          if (data && data.customer) {
            this.customer = data.customer;
            this.buildForm(this.fb);
            this.onFormChange();
          }
        });
    }

    this.countries$
      .pipe(
        takeUntil(this.destroy$),
        filter((data) => !isNullOrUndefined(data))
      )
      .subscribe((data) => {
        this.countryList = data;
      });

    this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((queryParams) => {
      if (queryParams && queryParams['notEditable']) {
        this.notEditable = true;
      }
    });

    this.setCompanyValidators();
  }

  private onFormChange() {
    // store address form data on changes
    this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((v) => {
      const formStateString = JSON.stringify(v);
      this.store.dispatch(new SaveFormState(ADDRESS_FORM_STATE_KEY, formStateString));
    });
  }

  addAddressErrorHandler = (invalidProperties: any) => {
    if (isNullOrUndefined(invalidProperties)) {
      return;
    }
    if (invalidProperties.FirstName) {
      this.form.get('firstName').setErrors({
        backendValidation: true,
        backendValidationMsg: invalidProperties.FirstName,
      });
      this.backendValidation = true;
    }
    if (invalidProperties.LastName) {
      this.form.get('lastName').setErrors({
        backendValidation: true,
        backendValidationMsg: invalidProperties.LastName,
      });
      this.backendValidation = true;
    }
    let showErrorPopUp = false;
    let keysChecked = 0;
    Object.keys(invalidProperties).forEach((key) => {
      keysChecked++;
      if (key !== 'FirstName' && key !== 'LastName') {
        showErrorPopUp = true;
      }
      if (keysChecked === Object.keys(invalidProperties).length && showErrorPopUp) {
        this.errorService.addErrors(409, null, JSON.stringify(invalidProperties));
      }
    });
    scrollToFirstInvalidElement();
  };

  async createBillingAddress(ignoreSuggestions = false) {
    if (this.form.valid) {
      this.postBtn.startLoading();
      const addressResponse = await this.validateAddress();
      if (addressResponse && addressResponse.invalidProperties) {
        this.invalidAddressMapping(addressResponse.invalidProperties);
      } else if (ignoreSuggestions) {
        this.post();
      } else {
        this.setAddressSuggestions(addressResponse);
      }
    } else {
      this.submitted = true;
      scrollToFirstInvalidElement();
    }
  }

  select(address: Address) {
    this.updateValuesOfAddress(address);
    this.post();
  }

  updateValuesOfAddress(add: Address) {
    if (add.city) {
      this.form.get('city').setValue(add.city);
      this.cityGeneralError = false;
    }
    if (add.street) {
      this.form.get('address').setValue(add.street);
      this.addressGeneralError = false;
    }
    if (add.streetNo) {
      this.form.get('streetNo').setValue(add.streetNo);
      this.addressNoGeneralError = false;
    }
    if (add.zip) {
      this.form.get('zipCode').setValue(add.zip);
      this.zipGeneralError = false;
    }
    if (add.country) {
      this.form.get('country').setValue(add.country);
    }
  }

  async post() {
    let updatedCustomer: User;
    this.backendValidation = false;
    if (this.addressId) {
      updatedCustomer = this.updateUserData();
    } else {
      updatedCustomer = this.addNewAddress();
    }
    if (this.isBillingForm) {
      this.store
        .dispatch(new AddNewInvoiceAddress(updatedCustomer.id, updatedCustomer.invoice_address))
        .pipe(
          takeUntil(this.destroy$),
          switchMap(() => {
            return this.customerService.addAddressError$;
          })
        )
        .subscribe((invalidProperties: any) => {
          if (invalidProperties) {
            this.addAddressErrorHandler(invalidProperties);
            this.postBtn.stopLoading();
          } else {
            const queryParams = this.notEditable ? { notEditable: true } : undefined;
            const currentRoute = `customer/edit/${this.customer.id}/billing`;
            this.store.dispatch(new ChangeCurrentRoute(currentRoute, false, queryParams));
            this.postBtn.stopLoading();
            this.router.navigate([currentRoute], { queryParams: queryParams });
          }
        });
    } else {
      this.store
        .dispatch(new AddNewShippingAddress(updatedCustomer.id, updatedCustomer.delivery_addres))
        .pipe(
          takeUntil(this.destroy$),
          switchMap(() => {
            return this.customerService.addAddressError$;
          })
        )
        .subscribe((invalidProperties: any) => {
          if (invalidProperties) {
            this.addAddressErrorHandler(invalidProperties);
            this.postBtn.stopLoading();
          } else {
            const queryParams = this.notEditable ? { notEditable: true } : undefined;
            const currentRoute = `customer/edit/${this.customer.id}/delivery`;
            this.store.dispatch(new ChangeCurrentRoute(currentRoute, false, queryParams));
            this.postBtn.stopLoading();
            this.router.navigate([currentRoute], { queryParams: queryParams });
          }
        });
    }
  }

  invalidAddressMapping(invalid: any) {
    if (invalid.City) {
      this.form.get('city').setErrors({
        backendValidation: true,
        backendValidationMsg: invalid.City,
      });
      this.submitted = true;
    }
    if (invalid.Street) {
      this.form.get('address').setErrors({
        backendValidation: true,
        backendValidationMsg: invalid.Street,
      });
      this.submitted = true;
    }
    if (invalid.streetNumber) {
      this.form.get('streetNo').setErrors({
        backendValidation: true,
        backendValidationMsg: invalid.streetNumber,
      });
      this.submitted = true;
    }
    if (invalid.ZipCode) {
      this.form.get('zipCode').setErrors({
        backendValidation: true,
        backendValidationMsg: invalid.ZipCode,
      });
      this.submitted = true;
    }
    if (invalid.General) {
      this.cityGeneralError = true;
      this.addressGeneralError = true;
      this.addressNoGeneralError = true;
      this.zipGeneralError = true;
      this.submitted = true;
    }
    this.postBtn.stopLoading();
    scrollToFirstInvalidElement();
  }

  async validateAddress() {
    const street = this.form.get('address').value as string;
    const streetNo = this.form.get('streetNo').value as string;
    const zipCode = this.form.get('zipCode').value as string;
    const city = this.form.get('city').value as string;
    const countryValue = this.form.get('country').value as string;
    let countryCode: string;
    if (countryValue) {
      const country = this.countryList.find((cl) => cl.value === countryValue);
      if (country) {
        countryCode = country.key;
      }
    }

    const address = <AddressDTO>{
      street: street,
      streetNumber: streetNo,
      zipCode: zipCode,
      city: city,
      country: countryCode,
    };
    return await this.customerService.addressValid(address).toPromise();
  }

  countryKey(country: string) {
    return this.store.selectSnapshot(CountrySelector.getCountriesIterable).find((t) => t.value === country).key;
  }

  private updateUserData(): User {
    const updateAddress = this.createAdrressData();
    const updatedCustomer: User = { ...this.customer };

    if (this.isBillingForm) {
      updatedCustomer.poossible_invoice_addresses = updatedCustomer.poossible_invoice_addresses.map((address: Address) => {
        if (address.id === +this.addressId) {
          return updateAddress;
        }

        return address;
      });

      if (updatedCustomer.invoice_address.id === updateAddress.id) {
        updatedCustomer.invoice_address = updateAddress;
      }
    } else {
      updatedCustomer.poossible_delivery_addresses = updatedCustomer.poossible_delivery_addresses.map((address: Address) => {
        if (address.id === +this.addressId) {
          return updateAddress;
        }

        return address;
      });

      if (updatedCustomer.delivery_addres.id === updateAddress.id) {
        updatedCustomer.delivery_addres = updateAddress;
      }
    }

    return updatedCustomer;
  }

  private addNewAddress(): User {
    const newAddress = this.createAdrressData();
    const updatedCustomer: User = { ...this.customer };

    if (this.isBillingForm) {
      updatedCustomer.poossible_invoice_addresses = [...(updatedCustomer.poossible_invoice_addresses || []), newAddress];
      updatedCustomer.invoice_address = newAddress;
    } else {
      updatedCustomer.poossible_delivery_addresses = [...(updatedCustomer.poossible_delivery_addresses || []), newAddress];
      updatedCustomer.delivery_addres = newAddress;
    }

    return updatedCustomer;
  }

  valueChanges(control: AbstractControl, value: string) {
    control.setValue(value);
  }

  setCompanyValidators() {
    if (this.form) {
      this.form
        .get('company')
        .valueChanges.pipe(distinctUntilChanged(), takeUntil(this.destroy$))
        .subscribe((companyName: string) => {
          if (companyName) {
            this.showCompanyFields = true;
          } else {
            this.showCompanyFields = false;
          }
        });
    }
  }

  genderSelect(gender: string) {
    this.updateDataForSelectedOption('gender', gender, 'Andrede');
  }

  titelSelect(title: string) {
    this.updateDataForSelectedOption('title', title, 'Titel');
  }

  countrySelect(country: string) {
    this.updateDataForSelectedOption('country', country, 'Land');
  }

  private createAdrressData(): Address {
    const data: Address = new Address(
      this.addressId ? +this.addressId : null,
      this.form.get('firstName').value,
      this.form.get('lastName').value,
      this.form.get('address').value,
      this.form.get('streetNo').value,
      this.form.get('zipCode').value,
      this.form.get('city').value,
      this.countryKey(this.form.get('country').value),
      this.form.get('company').value ? this.form.get('company').value : null,
      this.form.get('department').value ? this.form.get('department').value : null,
      this.form.get('note').value ? this.form.get('note').value : null,
      this.form.get('tax_number').value ? this.form.get('tax_number').value : null,
      this.form.get('title').value ? this.form.get('title').value : null,
      this.form.get('gender').value ? this.form.get('gender').value : null
    );

    return data;
  }

  private updateDataForSelectedOption(field: string, value: string, defaultValue: string) {
    if (value !== defaultValue) {
      this.form.get(field).patchValue(value);
    } else {
      this.form.get(field).patchValue(null);
    }
  }

  get f() {
    if (this.form) {
      return this.form.controls;
    }
  }

  private buildForm(fb: FormBuilder, address?: Address) {
    this.store
      .selectOnce(FormsSelectors.getFormState)
      .pipe(map((filterFn) => filterFn(ADDRESS_FORM_STATE_KEY)))
      .subscribe((value) => {
        const savedAddress = value ? JSON.parse(value) : undefined;
        this.form = fb.group({
          id: [address ? address.id : this.getProperty(savedAddress, 'id') || ''],
          firstName: [address ? address.first_name : this.getProperty(savedAddress, 'firstName') || '', Validators.required],
          lastName: [address ? address.last_name : this.getProperty(savedAddress, 'lastName') || '', Validators.required],
          address: [
            address ? (address.street ? address.street : '') : this.getProperty(savedAddress, 'address') || '',
            Validators.required,
          ],
          streetNo: [
            address ? (address.streetNo ? address.streetNo : '') : this.getProperty(savedAddress, 'streetNo') || '',
            Validators.required,
          ],
          zipCode: [address ? (address.zip ? address.zip : '') : this.getProperty(savedAddress, 'zipCode') || '', Validators.required],
          city: [address ? (address.city ? address.city : '') : this.getProperty(savedAddress, 'city') || '', Validators.required],
          country: [this.getProperty(savedAddress, 'country') || this.getCountryFromAddress(address), Validators.required],
          gender: [
            address ? address.gender : this.getProperty(savedAddress, 'gender') || '',
            [Validators.required, CustomValidators.validateGender],
          ],
          company: [address ? (address.company_name ? address.company_name : '') : this.getProperty(savedAddress, 'company') || ''],
          department: [
            address ? (address.company_department ? address.company_department : '') : this.getProperty(savedAddress, 'department') || '',
          ],
          tax_number: [
            address ? (address.company_tax_number ? address.company_tax_number : '') : this.getProperty(savedAddress, 'tax_number') || '',
          ],
          title: [address ? address.title : this.getProperty(savedAddress, 'title') || ''],
          note: [address ? address.note : this.getProperty(savedAddress, 'note') || ''],
        });
      });
  }

  private getCountryFromAddress(address?: Address) {
    if (isNullOrUndefined(address)) {
      return '';
    }

    const countries = this.store.selectSnapshot(CountrySelector.getCountriesIterable);
    const country = countries.find((t) => t.key === address.country);
    if (isNullOrUndefined(country)) {
      return address.country;
    }

    return country.value;
  }

  setAddressSuggestions(address: any) {
    this.addressSuggestions = [];
    if (address && Array.isArray(address) && address.length > 0) {
      const firstName = this.form.get('firstName').value;
      const lastName = this.form.get('lastName').value;
      let processedItems = 0;
      const addressesLength = address.length;
      address.forEach((add) => {
        processedItems++;
        const street = add.street ? add.street : (this.form.get('address').value as string);
        const streetNo = add.streetNumber ? add.streetNumber : (this.form.get('streetNo').value as string);
        const zipCode = add.zipCode ? add.zipCode : (this.form.get('zipCode').value as string);
        const city = add.city ? add.city : (this.form.get('city').value as string);
        const countryValue = add.country ? add.country : (this.form.get('country').value as string);

        if (city && street && streetNo && zipCode && countryValue) {
          this.addressSuggestions.push({
            id: 0,
            first_name: firstName as string,
            last_name: lastName as string,
            country: countryValue as string,
            city: city as string,
            street: street as string,
            streetNo: streetNo as number,
            zip: zipCode,
          });
        }
        if (processedItems === addressesLength && this.addressSuggestions.length === addressesLength) {
          this.cdrf.detectChanges();
          this.openSuggestions();
        }
      });
      this.postBtn.stopLoading();
    } else {
      this.post();
    }
  }

  getProperty(obj: Object, key: string) {
    return key.split('.').reduce((o, x) => {
      return typeof o === 'undefined' || o === null ? o : o[x];
    }, obj);
  }

  openSuggestions() {
    if (this.addressSuggestions && this.addressSuggestions.length > 0) {
      this.suggestions.openDialog();
    }
  }

  ngOnDestroy() {
    this.destroy$.next();
  }
}
