import { AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
import { marker } from "@biesbjerg/ngx-translate-extract-marker";
import { TitleService } from 'src/app/shared/services/title.service';
import { AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
import { PaymentService } from "src/app/shared/server-services/payment.service";
import { PaymentBillings, PaymentBillingsData, PaymentBillingsRecord, PaymentBuyCodeRecord, PaymentPreapreResult, PaymentPrepareData, PaymentPrepareResultRecord, PaymentRecordOrder, PaymentShopData, PaymentShopDataCurrencyType, PaymentShopDataProduct, PaymentShopDataProductPackage, PaymentShopDataProductPackageMethodType, PaymentShopDataProductPackageTargetLimitType, PaymentShopDataRecord } from 'src/app/shared/server-services/query-records/payment-records';
import { CurrencyPipe, KeyValue } from '@angular/common';
import { NanoValidators } from 'src/app/shared/nano-validators';
import { CountryCode } from 'src/app/shared/country-codes';
import { States } from 'src/app/shared/states';
import { PostalCodeRecord, PostalCodes } from 'src/app/shared/postal-codes';
import { MatStepper } from '@angular/material/stepper';
import { DialogService } from 'src/app/shared/services/dialog.service';
import { MatDialog } from '@angular/material/dialog';
import { EditBillingTemplateDialogComponent } from './edit-billing-template-dialog/edit-billing-template-dialog.component';
import { RouterHandler } from 'src/app/shared/services/router-handler.service';
import { EnvironmentService } from 'src/environments/environment.service';
import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper';
import { map, startWith } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';

@Component({
  selector: 'app-nano-license-buy',
  templateUrl: './nano-license-buy.component.html',
  styleUrls: ['./nano-license-buy.component.scss'],
  providers: [
    {
      provide: STEPPER_GLOBAL_OPTIONS,
      useValue: {showError: true},
    },
  ]
})
export class NanoLicenseBuyComponent implements OnInit, AfterViewInit {

  public countryCodes = CountryCode;
  public states = States;
  public postalCodes = PostalCodes;
  public filteredPostalCodes: PostalCodeRecord[] = [];
  public siteBase: string;

  @ViewChild('stepper', {static: false}) private stepper: MatStepper;

  shopData: PaymentShopDataRecord;
  paymentData: PaymentPrepareData;
  preparedPaymentData: PaymentPrepareResultRecord;
  paymentBillings: PaymentBillingsRecord[];
  selectedBilling: PaymentBillingsRecord;
  accountLicenseSlotLimit: number;

  isHun: boolean = localStorage.getItem("lang") == "hu";
  isBundle: boolean = false;
  isCompany: boolean = false;
  isWirePriceMet: boolean = false;
  canPaymentBeRecurring: boolean = false;
  taxErrors: boolean = false;
  taxCharMap: any;
  taxExamples: string[];
  saveToTemplate: boolean = false;
  paymentUrl: string;
  checkoutCompleted: boolean = false;

  basketForm = this.fb.group({
    currency: [this.getCurrencyFromLang(), Validators.required],
    product: [null, Validators.required],
    package: [null, Validators.required],
    productId: [null, Validators.required],
    packageId: [null, Validators.required],
    target: ['own', Validators.required],
    number: [1, [Validators.required, NanoValidators.isWholeNumber(), Validators.min(1)]]
  });

  billingInfoForm = this.fb.group({
    entity: ['individual', Validators.required],
    email: ['', [Validators.required, Validators.email, Validators.maxLength(255)]],
    name: ['', [Validators.required, Validators.maxLength(255)]],
    country: ['', [Validators.required]],
    state: ['', [Validators.maxLength(255)]],
    postal: ['', [Validators.required, Validators.maxLength(63)]],
    city: ['', [Validators.required, Validators.maxLength(255)]],
    address: ['', [Validators.required, Validators.maxLength(255)]]
  });

  paymentForm = this.fb.group({
    method: ['card', [Validators.required]],
    payment_recurring: [false],
    buy_code: [''],
    terms_of_service_accepted: [false, [Validators.requiredTrue]]
  });

  numberValidator: ValidatorFn;
  recurringMaxValidator: ValidatorFn;
  stateRequiredValidator: ValidatorFn = Validators.required;

  name_of_purchaser_control: FormControl = this.fb.control(null, [Validators.required, Validators.maxLength(255)]);
  tax_number_control: FormControl = this.fb.control(null, [Validators.required, Validators.maxLength(63)]);
  recurring_number_control: FormControl = this.fb.control(1, [Validators.required, NanoValidators.isWholeNumber(), Validators.min(1)]);
  simple_pay_accepted_control: FormControl = this.fb.control(false, [Validators.requiredTrue]);

  constructor(
    private titleService: TitleService,
    private fb: FormBuilder,    
    private paymentService: PaymentService,
    private currencyPipe: CurrencyPipe,
    private dialogService: DialogService,
    private dialog: MatDialog,
    private routerHandler: RouterHandler,
    private environmentService: EnvironmentService,
    private translate: TranslateService
  ) { 
    this.siteBase = this.environmentService.site_base;
    
    this.billingInfoForm.get('postal').valueChanges.subscribe(postal => {
      if(this.billingInfoForm.get('country').value != 'HU')
        this.filteredPostalCodes = [];
      else
        this.filteredPostalCodes = this.filterPostalCodes(postal.toString());
    });
  }

  private filterPostalCodes(value: string): PostalCodeRecord[] {

    return this.postalCodes.filter(postal => 
      postal.city.toLowerCase().includes(value.toLowerCase())
      || postal.postal.toString().startsWith(value.toLowerCase())
      || postal.county.toLowerCase().includes(value.toLowerCase())
      ).slice(0, 30);
  }

  ngOnInit(): void {
    this.paymentService.getPaymentShopData().then((shopData: PaymentShopData) => {
      this.shopData = shopData.response;
      this.accountLicenseSlotLimit = shopData.accountLicenseSlotLimit;
    });

    this.paymentService.getBuyCode().then((buyCode: PaymentBuyCodeRecord) => {
      if(buyCode.valid)
        this.paymentForm.get('buy_code').setValue(buyCode.buyCode);
    });

    this.getPaymentBillings();

    this.titleService.setTabTitle(marker("Licenses"));
  }

  ngAfterViewInit(): void{
    this.stepper.selectionChange.subscribe((change) => {
      if(change.selectedIndex < change.previouslySelectedIndex) { //stepping backwards

      }
      else{//stepping forward
        if(change.selectedIndex == 2){ //prepare order
          this.prepareOrder();
        }
        else if(change.selectedIndex == 3){ //finalize form
          this.finalizeOrder();
        }
      }

      const stepId = this.stepper._getStepLabelId(change.selectedIndex);
      let stepElement = document.getElementById(stepId);
      if (stepElement) {
        setTimeout(() => {
          
        let errorInput = document.querySelector('.mat-form-field-invalid');
        if (errorInput){
          stepElement = <HTMLElement>errorInput;
        }
          stepElement.scrollIntoView({ block: 'start', inline: 'nearest', behavior: 'smooth' });
        }, 250);
      }
    });
  }

  public scrollToError(): void{
    setTimeout(() => {
      let errorInput = document.querySelector('.mat-error');
      if(!errorInput) return;
      var scrollToElement = errorInput.parentElement;

      while(!scrollToElement.classList.contains("license-form-row")){
        scrollToElement = scrollToElement.parentElement;
      }

      scrollToElement.scrollIntoView({ block: 'start', inline: 'nearest', behavior: 'smooth' });
    }, 250);
  }

  private getPaymentBillings(){
    this.paymentService.getPaymentBillings().then((paymentBillingsResponse: PaymentBillings) => {
      this.paymentBillings = paymentBillingsResponse.response;
    });
  } 

  public getPrice(price: { [key in PaymentShopDataCurrencyType]: number }, months: number, free_months: number) : string{
    var selectedCurrency = (this.basketForm.get('currency').value as PaymentShopDataCurrencyType);
    var fullMonths = free_months ? months - free_months : months;
    var fullPrice = Math.round(price[selectedCurrency] * fullMonths);
    var pricePolicy = this.isHunCurrency ? '1.0-2' : '';
    var formattedPrice = this.currencyPipe.transform(fullPrice, selectedCurrency, 'symbol', pricePolicy);

    return `${formattedPrice}` + this.translate.instant(marker(" + VAT"));
  }

  public getGrossPrice(price: { [key in PaymentShopDataCurrencyType]: number }, months: number, free_months: number) : string{
    var selectedCurrency = (this.basketForm.get('currency').value as PaymentShopDataCurrencyType);
    var fullMonths = free_months ? months - free_months : months;
    var pricePolicy = this.isHunCurrency ? '1.0-2' : '';
    var formattedPrice = this.currencyPipe.transform(Math.round(price[selectedCurrency] * fullMonths * 1.27), selectedCurrency, 'symbol', pricePolicy);
    return `${ formattedPrice } bruttó`;
  }

  public selectProductPackage(productId: string, packageId: string, product: PaymentShopDataProduct): void{
    this.basketForm.get('productId').setValue(productId);
    this.basketForm.get('packageId').setValue(packageId);
    this.basketForm.get('product').setValue(product);

    this.updateCanBeRecurring();
    this.updateAmountValidator();
    this.updateIsWirePriceMet();
  }

  public get isHunCurrency(): boolean{
    var selectedCurrency = (this.basketForm.get('currency').value as PaymentShopDataCurrencyType);
    return selectedCurrency == PaymentShopDataCurrencyType.HUF;
  }

  public onCompare(_left: KeyValue<any, any>, _right: KeyValue<any, any>): number {
    return _left.value.months - _right.value.months;
  }

  public entitySelectionChanged(value: string): void{
    if(value == 'individual'){
      this.billingInfoForm.removeControl('name_of_purchaser');
      this.billingInfoForm.removeControl('tax_number');
    }
    else if(value == 'company'){
      this.billingInfoForm.addControl('name_of_purchaser', this.name_of_purchaser_control);
      this.billingInfoForm.addControl('tax_number', this.tax_number_control);
    }

    this.isCompany = value == 'company';
    this.taxErrors = false;
  }

  public targetSelectionChanged(e): void{
    this.isBundle = e.value == 'bundle';
    this.updateCanBeRecurring();
    this.updateAmountValidator();
  }

  public amountChanged(e): void{
    var amountControl = this.basketForm.get('number');
    if(!amountControl.valid) return;

    this.updateIsWirePriceMet();
  }

  public countrySelectionChanged(e): void{
    var stateControl = this.billingInfoForm.get('state');
    stateControl.setValue('');

    if(e.value == 'US'){
      if(!stateControl.hasValidator(this.stateRequiredValidator))
        this.billingInfoForm.get('state').addValidators(this.stateRequiredValidator);
    }
    else{
      if(e.value != 'HU'){
        this.filteredPostalCodes = [];
      }

      if(stateControl.hasValidator(this.stateRequiredValidator))
        this.billingInfoForm.get('state').removeValidators(this.stateRequiredValidator);
    }

    stateControl.updateValueAndValidity();
  }

  public onZipAutocompleteChange(event): void{
    var postal: PostalCodeRecord = event.option.value;
    this.billingInfoForm.get('postal').setValue(postal.postal.toString());
    this.billingInfoForm.get('city').setValue(postal.city);
  }

  public paymentRecurringSelectionChanged(e): void{
    if(e.value == true){
      this.paymentForm.addControl('simple_pay_accepted', this.simple_pay_accepted_control);
    }
    else{
      this.paymentForm.removeControl('simple_pay_accepted');
    }
  }

  public isCountryPrefixRequiredForTaxNumber(): boolean{
    return this.isCompany && this.shopData.constant_tax_number_prefixes.some(pre => pre == this.billingInfoForm.get('country').value);
  }

  public preFillBillingInfo():void{
    if(!this.selectedBilling){
      this.billingInfoForm.reset();
      this.billingInfoForm.get('entity').setValue('individual');
      this.entitySelectionChanged('individual');
      return;
    }

    if(this.billingInfoForm.get("entity").value != this.selectedBilling.entity)
      this.entitySelectionChanged(this.selectedBilling.entity);

    for(let prop of Object.keys(this.selectedBilling)){
      this.billingInfoForm.patchValue({[prop]: this.selectedBilling[prop]});
    }
  }

  public openBillingsDialog(): void{
    var dialogRef = this.dialog.open(EditBillingTemplateDialogComponent, {data: this.paymentBillings});
    dialogRef.afterClosed().subscribe((toDeleteBillings: number[]) => {
      if(toDeleteBillings){
        
        var promises: Promise<number>[] = [];

        for(let toDeleteBillingId of toDeleteBillings){
          promises.push(this.paymentService.deletePaymentBilling(toDeleteBillingId));
        }

        Promise.all(promises).then(() =>{
          this.dialogService.showSnackbar(marker("Billing template deleted!"));

          if(toDeleteBillings.some(deleteBillingId => this.selectedBilling?.billing_id == deleteBillingId)){ //if current selected billing is deleted
            this.saveToTemplate = false;
            this.selectedBilling = null;
          }

          this.getPaymentBillings();
        });
      }
      else{
        this.getPaymentBillings();
      }
    });
  }

  public saveTemplate(): void{
    var paymentBillingData: PaymentBillingsData = {
      address: this.paymentData.address,
      city: this.paymentData.city,
      country: this.paymentData.country,
      email: this.paymentData.email,
      entity: this.paymentData.entity,
      name: this.paymentData.name,
      name_of_purchaser: this.paymentData.name_of_purchaser,
      postal: this.paymentData.postal,
      state: this.paymentData.state,
      tax_number: this.paymentData.tax_number
    }

    if(this.selectedBilling){
      this.paymentService.editPaymentBilling(this.selectedBilling.billing_id, paymentBillingData).then(res => {
        this.dialogService.showSnackbar(marker("Billing template updated!"));
        this.getPaymentBillings();
        this.saveToTemplate = false;
      });
    }else{
      this.paymentService.savePaymentBilling(paymentBillingData).then(res => {
        this.dialogService.showSnackbar(marker("Billing data saved to templates!"));
        this.getPaymentBillings();
        this.saveToTemplate = false;
      });
    }    
  }

  public prepareOrder():void{    
    this.buildPrepareOrder();

    if(this.saveToTemplate){
      this.saveTemplate();
    }

    this.paymentService.preparePaymentOrder(this.paymentData).then((res: PaymentPreapreResult) => {

      if(this.billingInfoForm.get('tax_number')){
        this.taxErrors = false;
        this.billingInfoForm.get('tax_number').setErrors(null);
      }
      
      //this.stepper.next();
    }).catch(err => {
      console.error('prepare error', err, err.error);

      if(err.error?.field == "tax_number"){
        this.taxCharMap = err.error.example_char_map;
        this.taxExamples = err.error.examples;
        this.taxErrors = true;
        this.billingInfoForm.get('tax_number').setErrors({taxFormatError: true});
      }
      else if(err.error?.field == "email"){
        this.billingInfoForm.get('email').setErrors({emailFormat: true});
      }

      this.stepper.selectedIndex = 1;
    });
  }

  public finalizeOrder(): void{
    this.buildPrepareOrder();

    this.paymentService.preparePaymentOrder(this.paymentData).then((res: PaymentPreapreResult) => {

      this.preparedPaymentData = res.response as PaymentPrepareResultRecord;

      var paymentOrder: PaymentRecordOrder = {
        language: localStorage.getItem("lang"),
        order: this.paymentData,
        orderHash: this.preparedPaymentData.order_hash
      };
  
      this.paymentService.startPaymentOrder(paymentOrder).then(res => {
        this.paymentUrl = (<any>res).response.payment_url;
        //this.stepper.next();
      }).catch(err => console.error('payment url error', err));
    }).catch( err =>{
      console.error('prepare error', err, err.error);

      if(err.error?.field == "tax_number"){
        this.taxCharMap = err.error.example_char_map;
        this.taxExamples = err.error.examples;
        this.taxErrors = true;
        this.billingInfoForm.get('tax_number').setErrors({taxFormatError: true});
      }
      else if(err.error?.field == "email"){
        this.billingInfoForm.get('email').setErrors({emailFormat: true});
      }

      this.stepper.selectedIndex = 1;
    });
  }

  public updateBuyCode(): void{
    var buyCode = this.paymentForm.get('buy_code').value;
    this.paymentService.editBuyCode(buyCode).then((res) => {
      this.dialogService.showSnackbar(marker("Discount code updated!"));
      this.paymentForm.get('buy_code').markAsPristine();
    })
    .catch(err => {
      this.dialogService.openAlertDialog(marker("Invalid discount code"), marker("The provided discount code is not valid. Please use a correct discount code."));
    });
  }

  private buildPrepareOrder(): void{
    var paymentData = {
      address: this.billingInfoForm.get('address').value,
      city: this.billingInfoForm.get('city').value,
      country: this.billingInfoForm.get('country').value,
      currency: this.basketForm.get('currency').value,
      email: this.billingInfoForm.get('email').value,
      entity: this.billingInfoForm.get('entity').value,
      method: this.paymentForm.get('method') ? this.paymentForm.get('method').value : PaymentShopDataProductPackageMethodType.CARD,
      name: this.billingInfoForm.get('name').value,
      name_of_purchaser: this.billingInfoForm.get('name_of_purchaser')?.value ?? "",
      number: parseInt(this.basketForm.get('number').value),
      package: this.basketForm.get('packageId').value,
      postal: this.billingInfoForm.get('postal').value,
      product: this.basketForm.get('productId').value,
      recurring_number: this.paymentForm.get('recurring_number') && this.paymentForm.get('payment_recurring').value == true ? parseInt(this.paymentForm.get('recurring_number').value) : 0,
      state: this.billingInfoForm.get('state').value,
      target: this.basketForm.get('target').value,
      tax_number: this.billingInfoForm.get('tax_number')?.value ?? "", 
    };

    if(this.isCountryPrefixRequiredForTaxNumber())
      paymentData.tax_number = paymentData.country + paymentData.tax_number;

    this.paymentData = paymentData;
  }

  private updateIsWirePriceMet(){
    var product = this.basketForm.get('product')?.value;

    if(!product)
      return;

    var packageId = this.basketForm.get('packageId').value;
    var productPackage = product.packages[packageId];

    if(!productPackage.wire_minimum_price) 
      return;

    var selectedCurrency = (this.basketForm.get('currency').value as PaymentShopDataCurrencyType);
    var amount = this.basketForm.get('number').value;

    var fullPrice = Math.round((product.price[selectedCurrency] * productPackage.months)) * amount;
    var wire_minimum_price = productPackage.wire_minimum_price[selectedCurrency] as number;

    this.isWirePriceMet = fullPrice >= wire_minimum_price;
    if(!this.isWirePriceMet)
      this.paymentForm.get('method').setValue('card');
  }

  private updateAmountValidator(): void{
    var product = this.basketForm.get('product').value;
    if(!product) 
      return;
    var packageId = this.basketForm.get('packageId').value;
    var selectedTarget = this.basketForm.get('target').value;
    var basketNumberControl = this.basketForm.get('number');
    var maxNumber = product.packages[packageId].target_limits[selectedTarget];

    if(this.numberValidator)
      basketNumberControl.removeValidators(this.numberValidator);

    this.numberValidator = Validators.max(maxNumber);
    basketNumberControl.addValidators(this.numberValidator);
    basketNumberControl.updateValueAndValidity();
  }

  private updateCanBeRecurring(): void{
    var product = this.basketForm.get('product').value;
    if(!product) return;
    var packageId = this.basketForm.get('packageId').value;
    var ownMayBeRecurring = product.packages[packageId].own_may_be_recurring;
    var selectedTarget = this.basketForm.get('target').value;

    this.canPaymentBeRecurring = ownMayBeRecurring && selectedTarget == PaymentShopDataProductPackageTargetLimitType.OWN;

    if(this.canPaymentBeRecurring){
      this.paymentForm.addControl('recurring_number', this.recurring_number_control);
      this.recurringMaxValidator = Validators.max(this.shopData.recurring_max);
      this.recurring_number_control.addValidators(this.recurringMaxValidator);
      this.recurring_number_control.updateValueAndValidity();
    }
    else{
      this.paymentForm.removeControl('recurring_number');
      this.paymentForm.get('payment_recurring').setValue(false);
    }
  }

  private getCurrencyFromLang(): string{
    switch(localStorage.getItem("lang")){
      case "hu": return "HUF";
      case "en": return "EUR";
      default: return "EUR";
    }
  }

  private taxNumberValidator(exampleArray: string[], exampleCharMap: any): ValidatorFn{
    return (control: AbstractControl): ValidationErrors | null => {
      var valid: boolean = true;

      //check lengths
      valid = exampleArray.some(sample => sample.length == control.value.length);

      //length ok, check format
      if(valid){
        for(let sample in exampleArray){
          for(let i = 0; i < control.value.length; i++){
            //TODO: check if characters match with any of the samples
          }
        }
      }

      return valid ? null : { taxNumber: {value: control.value} };
    };  
  }
}
