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 { FleioId, 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 { 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 { CallbackAction } from '@objects-view/actions/callback-action';
import { RouteHelper } from '@shared/ui-api/route-helper';
import { DomainUtils } from '@shared/plugins/utils/domain-utils';
import { ContactEditFormComponent } from '@shared/plugins/common-tabs/domains/contacts/contact-edit-form/contact-edit-form.component';
import { IPriceCyclesModel } from '@fleio-api/plugins/domains/model/price-cycle.model';
import { CurrencyFormatterPipe } from '@shared/ui/common/pipes/currency-formatter.pipe';
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 {
  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: [''],
    nameserver2: [''],
    nameserver3: [''],
    nameserver4: [''],
    domain_contact_type: [''],
    contact: [''],
  });
  nameCtrl = this.newDomainForm.controls.name;

  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
  clientAsContactTxt = $localize`Clients are used for invoicing, accounting and as domain registration details`;
  selectDomainContactTxt = $localize`Contacts are only used for domain registration details`;
  editContactTooltip = $localize`Edit domain contact`;
  addContactTooltip = $localize`Add a new domain contact`;
  contactUnselectedPlaceholder = $localize`Select contact ...`;
  contactSelectedPlaceholder = $localize`Contact`;

  private currencyPipe: CurrencyFormatterPipe;
  public prices: IPriceCyclesModel;

  domainNameQueryParam = this.activatedRoute.snapshot.params.domain_name || null;
  preselectedClientId = this.activatedRoute.snapshot.params.client || null;

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

  ngOnInit() {
    super.ngOnInit();

    this.newDomainForm.patchValue({
      domain_contact_type: 'client',
    });

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

    if (this.preselectedClientId) {
      this.clientsApi.get(this.preselectedClientId).subscribe(client => {
        this.newDomainForm.controls.client.setValue(client);
      });
    }

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

    this.newDomainForm.controls.client.valueChanges.subscribe((value) => {
      this.domainAvailability = undefined;
      this.newDomainForm.controls.contact.setValue('');
      if (value && value.id) {
        // make sure client has all fields for edit
        this.clientsApi.get(value.id).subscribe(client => {
          this.newDomainForm.controls.client.setValue(client, { emitEvent: false });
          if (this.domainNameQueryParam && this.newDomainForm.controls.name.value === this.domainNameQueryParam) {
            this.checkDomainName();
          }
        });
      }
    });

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

    this.newDomainForm.controls.contact.valueChanges.subscribe(() => {
      const contact = this.newDomainForm.controls.contact.value;
      if (contact && contact.id) {
        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;
        });
      }
    });

    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();
      }
    });

    const finalRouteSnapshot = (new RouteHelper(this.activatedRoute)).getFinalRoute().snapshot;
    if (finalRouteSnapshot.queryParams.client) {
      this.clientsApi.get(finalRouteSnapshot.queryParams.client as FleioId).subscribe(client => {
        this.newDomainForm.controls.client.setValue(client);
      });
    }
  }

  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_id) {
      domain.contact_id = domain.contact_id.id;
    }

    this.objectController.loading = true;
    this.orderDomainApiService.registerDomain(domain).subscribe({
      next: (order) => {
        this.notificationService.showMessage($localize`Domain order placed`);
        this.router.navigateByUrl(this.config.getPanelUrl(
          `orders/${order.order_id}`
        )).then(()=>{});
      },
      error: (error) => {
        this.objectController.loading = false;
        if (error.error) {
          this.setErrors(error.error);
        }
        this.notificationService.showMessage($localize`Failed to place domain order`);
      }
    });
  }

  checkDomainName() {
    this.checkingDomain = true;
    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;

          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(() => {
      this.checkingDomain = false;
      this.objectController.loading = 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.authSvc.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.authSvc.currencyDisplayMode,
      );
      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.authSvc.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: '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, { emitEvent: false });
              this.checkDomainName();
            }
          );
        }
    });
  }
}
