import { Component, OnInit } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { of } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
import { ConfigService } from '@shared/config/config.service';
import { ClientsApiService } from '@fleio-api/client-user/client/clients-api.service';
import { IBaseFleioObjectModel } from '@fleio-api/base-model/base-fleio-object.model';
import { DomainApiService } from '@fleio-api/plugins/domains/domain-api.service';
import { OrderDomainApiService } from '@fleio-api/plugins/domains/order-domain-api.service';
import { IDomainAvailabilityInfoModel } from '@fleio-api/plugins/domains/model/domain-availability-info.model';
import { NotificationService } from '@shared/ui-api/notification.service';
import { DomainUtils } from '@shared/plugins/utils/domain-utils';
import { DomainContactsApiService } from '@fleio-api/plugins/domains/domain-contacts-api.service';
import { ICustomFieldsStatusModel } from '@fleio-api/plugins/domains/model/custom-fields-status.model';
import { MatDialog } from '@angular/material/dialog';
import { ObjectDetailsDialogComponent } from '@objects-view/object-details-dialog/object-details-dialog.component';
import { StaticObjectController } from '@objects-view/static-object-controller';
import { ContactEditFormComponent } from '@shared/plugins/common-tabs/domains/contacts/contact-edit-form/contact-edit-form.component';
import { CallbackAction } from '@objects-view/actions/callback-action';
import { IPluginComponent } from '@shared/plugins/interfaces/plugin-component';
import { IPriceCyclesModel } from '@fleio-api/plugins/domains/model/price-cycle.model';
import { AuthService } from '@shared/auth/auth.service';
import { CurrencyFormatterPipe } from '@shared/ui/common/pipes/currency-formatter.pipe';
import { CartApiService } from '@fleio-api/billing/cart/cart-api.service';
import {
  ClientEditFormComponent
} from '@shared/common-tabs/clients-users/clients/client-edit-form/client-edit-form.component';

@Component({
  selector: 'app-domain-register-form',
  templateUrl: './domain-register-form.component.html',
  styleUrls: ['./domain-register-form.component.scss']
})
export class DomainRegisterFormComponent extends DetailsFormBase<IBaseFleioObjectModel> implements OnInit, IPluginComponent {
  public data: {
    domain_name: string;
  };
  newDomainForm = this.formBuilder.group({
    client: ['', Validators.required],
    name: ['', Validators.required],
    years: [0, Validators.required],
    dns_management: [false],
    email_forwarding: [false],
    id_protection: [false],
    use_default_nameservers: [false],
    nameserver1: ['', Validators.maxLength(256)],
    nameserver2: ['', Validators.maxLength(256)],
    nameserver3: ['', Validators.maxLength(256)],
    nameserver4: ['', Validators.maxLength(256)],
    domain_contact_type: [''],
  });
  nameCtrl = this.newDomainForm.controls.name;

  anonymousUser = false;

  domainUtils = new DomainUtils();

  years = this.newDomainForm.controls.years;
  dns_management = this.newDomainForm.controls.dns_management;
  email_forwarding = this.newDomainForm.controls.email_forwarding;
  id_protection = this.newDomainForm.controls.id_protection;
  domain_contact_type = this.newDomainForm.controls.domain_contact_type;

  domainAvailability: IDomainAvailabilityInfoModel;
  checkingDomain: boolean;
  checkingCustomFields = false;

  customFieldsStatus: ICustomFieldsStatusModel;

  dnsManagementPrice: string;
  dnsManagementAvailable: boolean;
  emailForwardingPrice: string;
  emailForwardingAvailable: boolean;
  idProtectionPrice: string;
  idProtectionAvailable: boolean;
  domainCheckedAndAvailable = false; // variable that says if domain was checked and is available

  contactUnselectedPlaceholder = $localize`Select contact ...`;
  contactSelectedPlaceholder = $localize`Contact`;

  private currencyPipe: CurrencyFormatterPipe;
  public prices: IPriceCyclesModel;

  domainNameQueryParam = this.activatedRoute.snapshot.queryParams.domain_name || null;

  constructor(
    private formBuilder: UntypedFormBuilder, private domainApiService: DomainApiService,
    private router: Router, public config: ConfigService, private activatedRoute: ActivatedRoute,
    private orderDomainApiService: OrderDomainApiService,
    private notificationService: NotificationService,
    private matDialog: MatDialog,
    private clientsApi: ClientsApiService,
    public domainContactsApi: DomainContactsApiService,
    public authService: AuthService,
    private cartApiService: CartApiService,
  ) {
    super();
    this.currencyPipe = new CurrencyFormatterPipe(this.config);
  }

  ngOnInit() {
    if (this.data?.domain_name) {
      // component is used without an objectController
      this.ignoreObjectController = true;
    }
    super.ngOnInit();

    if (this.data) {
      if (this.data.domain_name) {
        this.newDomainForm.controls.name.setValue(this.data.domain_name);
        this.checkDomainName();
      }
    }
    if (this.domainNameQueryParam) {
      this.newDomainForm.controls.name.setValue(this.domainNameQueryParam);
      this.checkDomainName();
    }

    if (this.authService.feature('ui.staff')) {
      this.cartApiService.list().subscribe((value: any) => {
        this.newDomainForm.patchValue({
          domain_contact_type: 'client',
          client: value.client_data,
        });
      });
    } else {
      if (this.authService?.userData?.user?.clients) {
        this.clientsApi.get(this.config.activeClientId).subscribe(client => {
          this.newDomainForm.patchValue({
            domain_contact_type: 'client',
            client: client,
          });
        });
      }
    }

    this.newDomainForm.controls.name.valueChanges.subscribe(() => {
      this.domainCheckedAndAvailable = false;
    });

    this.newDomainForm.controls.client.valueChanges.subscribe((client) => {
      // re-check custom fields
      if (client && client.id && this.newDomainForm.controls.name.value) {
        this.checkingCustomFields = true;
        this.orderDomainApiService.checkCustomFields(
          this.newDomainForm.controls.client.value.id,
          0,
          this.newDomainForm.controls.name.value,
        ).subscribe(customFieldsStatus => this.customFieldsStatus = customFieldsStatus).add(() => {
          this.checkingCustomFields = false;
        });
      }
      if (this.newDomainForm.contains('contact')) {
        this.newDomainForm.controls.contact.setValue('');
      }
    });

    this.newDomainForm.controls.name.valueChanges.subscribe(() => {
      this.domainAvailability = undefined;
    });

    this.domain_contact_type.valueChanges.subscribe(value => {
      if (value === 'contact') {
        if (!this.newDomainForm.contains('contact')) {
          this.newDomainForm.addControl(
            'contact', this.formBuilder.control('', Validators.required)
          );
        }
        this.newDomainForm.controls.contact.valueChanges.subscribe(() => {
          const contact = this.newDomainForm.controls.contact.value;
          if (contact && contact.id && this.newDomainForm.controls.name.value) {
            this.checkingCustomFields = true;
            this.orderDomainApiService.checkCustomFields(
              this.newDomainForm.controls.client.value.id,
              contact.id,
              this.newDomainForm.controls.name.value,
            ).subscribe(customFieldsStatus => this.customFieldsStatus = customFieldsStatus).add(() => {
              this.checkingCustomFields = false;
            });
          }
        });
      } else {
        if (this.newDomainForm.contains('contact')) {
          this.newDomainForm.removeControl('contact');
        }
      }
    });

    this.newDomainForm.controls.use_default_nameservers.valueChanges.subscribe(value => {
      if (value) {
        this.newDomainForm.controls.nameserver1.disable();
        this.newDomainForm.controls.nameserver2.disable();
        this.newDomainForm.controls.nameserver3.disable();
        this.newDomainForm.controls.nameserver4.disable();
      } else {
        this.newDomainForm.controls.nameserver1.enable();
        this.newDomainForm.controls.nameserver2.enable();
        this.newDomainForm.controls.nameserver3.enable();
        this.newDomainForm.controls.nameserver4.enable();
      }
    });

    if (!this.authService?.userData?.user?.id) {
      this.anonymousUser = true;
      this.newDomainForm.controls.client.clearValidators();
    }
  }

  registerDomain() {
    if (!this.domainCheckedAndAvailable) {
      // a domain has to be checked first before submitting the form
      this.checkDomainName();
      return of(null);
    }
    this.validate();
    if (this.formGroup.invalid) {
      this.displayControlErrors();
      return of(null);
    }
    const domain = this.newDomainForm.getRawValue();

    domain.client = domain.client.id;
    if (domain.contact) {
      domain.contact_id = domain.contact.id;
    }

    if (this.objectController) {
      this.objectController.loading = true;
    }
    this.orderDomainApiService.registerDomain(domain, true).subscribe(
      () => {
        this.notificationService.showMessage($localize`Domain added to cart`);
        if (this.authService.feature('ui.staff')) {
          this.router.navigateByUrl(this.config.getPanelUrl(
            `orders/cart`
          )).then(() => {
          });
        } else {
          this.router.navigateByUrl(this.config.getPanelUrl(
            `billing/cart`
          )).then(() => {
          });
        }
      },
      (error) => {
        if (this.objectController) {
          this.objectController.loading = false;
        }
        if (error.error) {
          this.setErrors(error.error);
        }
        this.notificationService.showMessage($localize`Failed to add domain to cart`);
      }
    );
  }

  checkDomainName() {
    this.checkingDomain = true;
    if (this.objectController) {
      this.objectController.loading = true;
    }
    const domainName = this.newDomainForm.controls.name.value;
    this.router.navigate([], {
      relativeTo: this.activatedRoute,
      queryParams: {
        domain_name: domainName
      },
      queryParamsHandling: 'merge',
    });
    this.orderDomainApiService.isAvailableForRegistration(
      this.newDomainForm.controls.client.value.id,
      domainName,
    ).subscribe(
      domainAvailability => {
        this.domainAvailability = domainAvailability;
        if (domainAvailability.available) {
          this.prices = domainAvailability.premium_prices || domainAvailability.prices;
          this.newDomainForm.controls.name.setValue(domainAvailability.adjusted_name);
          if (domainAvailability.config.enable_default_nameservers) {
            this.newDomainForm.controls.use_default_nameservers.enable();
            this.newDomainForm.controls.use_default_nameservers.setValue(true);
            this.setDefaultNameservers(domainAvailability);
          } else {
            this.newDomainForm.controls.use_default_nameservers.setValue(false);
            this.newDomainForm.controls.use_default_nameservers.disable();
          }
          this.updateOptionPrices(domainAvailability);
          this.newDomainForm.controls.domain_contact_type.setValue('client');
          this.customFieldsStatus = null;

          if (!this.anonymousUser) {
            this.orderDomainApiService.checkCustomFields(
              this.newDomainForm.controls.client.value.id,
              0,
              this.newDomainForm.controls.name.value,
            ).subscribe(customFieldsStatus => this.customFieldsStatus = customFieldsStatus);
          }
          this.domainCheckedAndAvailable = true;
        }
      },
      () => {
        this.notificationService.showMessage(
          $localize`Failed to check if domain is available for registration`,
        );
        this.domainAvailability = {
          available: false,
          error: $localize`Error checking domain`,
        };
      },
    ).add(() => {
      if (this.objectController) {
        this.objectController.loading = false;
      }
      this.checkingDomain = false;
    });
  }

  updateOptionPrices(domainAvailability?: IDomainAvailabilityInfoModel) {
    if (domainAvailability) {
      this.domainAvailability = domainAvailability;
    }
    if (this.domainAvailability.dns_prices.prices_per_years[this.years.value] === null) {
      this.dnsManagementPrice = '';
      this.dnsManagementAvailable = false;
      this.dns_management.setValue(false);
      this.dns_management.disable();
    } else {
      this.dnsManagementPrice = this.currencyPipe.transform(
        this.domainAvailability.dns_prices.prices_per_years[this.years.value],
        this.domainAvailability.dns_prices.currency.code,
        this.authService.currencyDisplayMode,
      );
      this.dnsManagementAvailable = true;
      this.dns_management.enable();
    }
    if (this.domainAvailability.email_prices.prices_per_years[this.years.value] === null) {
      this.emailForwardingPrice = '';
      this.emailForwardingAvailable = false;
      this.email_forwarding.setValue(false);
      this.email_forwarding.disable();
    } else {
      this.emailForwardingPrice = this.currencyPipe.transform(
        this.domainAvailability.email_prices.prices_per_years[this.years.value],
        this.domainAvailability.email_prices.currency.code,
        this.authService.currencyDisplayMode,
      );
      this.emailForwardingAvailable = true;
      this.email_forwarding.enable();
    }
    if (this.domainAvailability.id_prices.prices_per_years[this.years.value] === null) {
      this.idProtectionPrice = '';
      this.idProtectionAvailable = false;
      this.id_protection.setValue(false);
      this.id_protection.disable();
    } else {
      this.idProtectionPrice = this.currencyPipe.transform(
        this.domainAvailability.id_prices.prices_per_years[this.years.value],
        this.domainAvailability.id_prices.currency.code,
        this.authService.currencyDisplayMode,
      );
      this.idProtectionAvailable = true;
      this.id_protection.enable();
    }
  }

  setDefaultNameservers(domainAvailability?: IDomainAvailabilityInfoModel) {
    if (domainAvailability) {
      this.domainAvailability = domainAvailability;
    }
    this.newDomainForm.controls.nameserver1.setValue(this.domainAvailability.config.default_nameserver1);
    this.newDomainForm.controls.nameserver2.setValue(this.domainAvailability.config.default_nameserver2);
    this.newDomainForm.controls.nameserver3.setValue(this.domainAvailability.config.default_nameserver3);
    this.newDomainForm.controls.nameserver4.setValue(this.domainAvailability.config.default_nameserver4);
  }

  editDomainContact() {
    const objectController = new StaticObjectController(
      {
        tabs: [
          {
            tabName: $localize`Edit contact`,
            tabIdentifier: 'edit-contact',
            component: ContactEditFormComponent,
          },
        ],
      },
      this.newDomainForm.controls.contact.value,
      {
        createOptions: this.activatedRoute.snapshot.data.contactCreateOptions,
      }
    );

    this.matDialog.open(
      ObjectDetailsDialogComponent, {
        data: {
          dialogTitle: $localize`Edit domain contact`,
          objectController,
          actions: [new CallbackAction({name: $localize`Save`, primary: true, needsResult: true})]
        },
        autoFocus: false,
      }).afterClosed().subscribe((value) => {
        if (value) {
          this.domainContactsApi.get(objectController.object.id).subscribe(
            (contact) => {
              this.newDomainForm.controls.contact.setValue(contact);
            }
          );
        }
    });
  }

  createDomainContact() {
    const objectController = new StaticObjectController(
      {
        tabs: [
          {
            tabName: $localize`Create contact`,
            tabIdentifier: 'create-contact',
            component: ContactEditFormComponent,
          },
        ],
      },
      null,
      {
        createOptions: this.activatedRoute.snapshot.data.contactCreateOptions,
      }
    );

    this.matDialog.open(
      ObjectDetailsDialogComponent, {
        data: {
          dialogTitle: $localize`Create domain contact`,
          objectController,
          actions: [new CallbackAction({name: $localize`Create`, primary: true, needsResult: true})]
        },
        autoFocus: false,
      }).afterClosed().subscribe(() => {
      this.domainContactsApi.list().subscribe(
        () => {
          this.newDomainForm.controls.contact.setValue('');
        }
      );
    });
  }

  editClient() {
    const objectController = new StaticObjectController(
      {
        tabs: [
          {
            tabName: $localize`Edit client`,
            tabIdentifier: 'edit-client',
            component: ClientEditFormComponent,
          },
        ],
      },
      this.newDomainForm.controls.client.value,
      {
        createOptions: this.activatedRoute.snapshot.data.clientCreateOptions,
      }
    );

    this.matDialog.open(
      ObjectDetailsDialogComponent, {
        data: {
          dialogTitle: $localize`Edit client`,
          objectController,
          unlimitedWidth: true,
          actions: [new CallbackAction({name: $localize`Save`, primary: true, needsResult: true})]
        },
        autoFocus: false,
      }).afterClosed().subscribe((value) => {
        if (value) {
          this.clientsApi.get(objectController.object.id).subscribe(
            (client) => {
              this.newDomainForm.controls.client.setValue(client);
            }
          );
        }
    });
  }
}
