import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { IInstanceModel } from '@fleio-api/openstack/model/instance.model';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ConfigService } from '@shared/config/config.service';
import { InstancesApiService } from '@fleio-api/openstack/instance/instances-api.service';
import { Observable, of, pairwise } from 'rxjs';
import { debounceTime, startWith } from 'rxjs/operators';
import { ClientsApiService } from '@fleio-api/client-user/client/clients-api.service';
import { IClientModel } from '@fleio-api/client-user/model/client.model';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { IInstanceCreateOptions } from '@fleio-api/openstack/model/instance-create-options';
import { ISelectedBootSource } from './boot-source-select/boot-source-select.component';
import { IFlavorModel } from '@fleio-api/openstack/model/flavor.model';
import { NotificationService } from '@shared/ui-api/notification.service';
import { FlavorsAsCardsComponent } from '@shared/fleio-data-controls/flavors-as-cards/flavors-as-cards.component';
import { ImagesApiService } from '@fleio-api/openstack/image/image-api.service';
import { FleioObjectsList } from '@fleio-api/fleio-objects-list';
import { MatDialog } from '@angular/material/dialog';
import { FleioId } from '@fleio-api/base-model/base-fleio-object.model';
import {
  SshKeyAddDialogComponent
} from '@shared/common-dialogs/openstack/ssh-keys/ssh-key-add-dialog/ssh-key-add-dialog.component';
import { ValidatorsHelper } from '@shared/ui-api/helpers/validators-helper';
import { IIcon } from '@shared/ui/common/interfaces/icon';

@Component({
  selector: 'app-instance-create-form',
  templateUrl: './instance-create-form.component.html',
  styleUrls: ['./instance-create-form.component.scss']
})
export class InstanceCreateFormComponent extends DetailsFormBase<IInstanceModel> implements OnInit {
  @ViewChild('nicsSelect', {static: true}) nicsSelect;
  @ViewChild('bootSourceSelectComponent', {static: false}) bootSourceSelectComponent;
  @ViewChild('flavorsAsCards') flavorsAsCardsComponent: FlavorsAsCardsComponent;
  instanceForm = this.formBuilder.group({
    name: ['', Validators.required],
    client: ['', Validators.required],
    region: ['', Validators.required],
    flavor: ['', Validators.required],
    user_data: [null],
    nics: [[], Validators.required],
    ssh_keys: [[]],
    root_password: [''],
  });
  clients: FleioObjectsList<IClientModel>;
  clientControl = this.instanceForm.controls.client;
  createOptions = this.activatedRoute.snapshot.data.createOptions as IInstanceCreateOptions;
  selectedBootSource: ISelectedBootSource | null;
  showUserData = false;
  showPasswordText = false;
  selectedFlavor: IFlavorModel | null;
  flavorsAsCards = this.flavorsAsCardsActive();
  clickedSave = false;
  imageLaunchId = this.activatedRoute.snapshot.queryParams.image_id || null;
  clientLaunchId = this.activatedRoute.snapshot.queryParams.client_id || null;
  shareImage = false;
  holdButtonText = $localize`Hold button to reveal password`;
  selectClient = this.config.authService.feature('ui.openstack.instances.select-client');
  createInClientProject = true;
  estimatedCost: string;
  estimatedCostLoading = false;
  passwordControl = this.instanceForm.controls.root_password;
  passwordLabel = $localize`Root password`;
  passwordHint = null;
  sshKeysHint = null;
  showPasswordInput = true;
  canAssignFloatingIp = false;
  floatingIpNetworks = [];
  assignFloatingIPInfoIcon: IIcon = {
    name: 'info',
    verticalAlign: 'middle',
    fontSize: '20px',
    marginLeft: '5px',
    marginTop: '9px',
    tooltipDirection: 'right',
    tooltip: $localize`Automatically creates and assigns a floating IP.`
  };

  flavorsAsCardsActive() {
    if (this.config && this.config.current) {
      return this.config.current.settings.instanceForm.flavorsAsCards;
    }
    return false;
  }

  constructor(
    private formBuilder: UntypedFormBuilder, private instancesApiService: InstancesApiService, private router: Router,
    public config: ConfigService, private activatedRoute: ActivatedRoute, public clientApiService: ClientsApiService,
    private notificationService: NotificationService, changeDetectorRef: ChangeDetectorRef,
    private imagesApiService: ImagesApiService,
    private matDialog: MatDialog,
  ) {
    super(null, changeDetectorRef);
  }

  changeDestination(value) {
    // createInClientProject
    if (value === false) {
      this.clientControl.setValue('', {emitEvent: false});
      this.clientControl.clearValidators();
      this.clientControl.reset(null, {emitEvent: false});
    } else {
      this.clientControl.setValidators(Validators.required);
      this.clientControl.reset(null);
    }
    this.refreshCreateOptions();
    this.estimatedCost = null;
  }

  private saveInstance(): Observable<IActionResult> {
    this.clickedSave = true;
    this.validate();
    if (this.formGroup.invalid) {
      this.displayControlErrors();
    }
    for (const control in this.instanceForm.controls) {
      if (this.instanceForm.controls[control].invalid) {
        if (!(this.flavorsAsCards && control === 'flavor')) {
          // do not validate flavors as cards selection or boot source and show
          // message if other field errors are present
          return of(null);
        }
      }
    }
    if (this.flavorsAsCards) {
      if (this.flavorsAsCardsComponent.selectedFlavor) {
        this.instanceForm.controls.flavor.setValue(this.flavorsAsCardsComponent.selectedFlavor.id);
      } else {
        this.notificationService.showMessage($localize`You must choose a flavor!`);
        return of(null);
      }
    }
    if (!this.selectedBootSource || (this.selectedBootSource && !this.selectedBootSource.objectId)) {
      this.notificationService.showMessage($localize`You must choose a boot source!`);
      return of(null);
    }
    const value = this.instanceForm.value;
    let volumeType = this.selectedBootSource.storageType;
    if (this.selectedBootSource.type === 'volume') {
      volumeType = '';
    }
    value.boot_source = {
      create_new_volume: this.selectedBootSource.createNewVolume,
      delete_on_termination: this.selectedBootSource.deleteOnTermination,
      source_id: this.selectedBootSource.objectId,
      source_type: this.selectedBootSource.type,
      volume_size: this.selectedBootSource.volumeSize,
      volume_type: volumeType,
      cinder_az: this.selectedBootSource.cinderAZ === 'fleio-auto' ? null : this.selectedBootSource.cinderAZ,
    }; // set boot_source based on selectedBootSource options sent from the boot-source-select-component
    if (value.client && value.client.id) {
      value.client = value.client.id;
    }
    if (value.hasOwnProperty('ssh_keys') && value.ssh_keys.length === 0) {
      delete value.ssh_keys;
    } else {
      value.ssh_keys = JSON.stringify(value.ssh_keys);
    }
    if (value.root_password === '') {
      delete value.root_password;
    }
    if (this.shareImage === true) {
      value.share_image = true;
    }
    if (!this.createInClientProject) {
      value.create_as_admin = true;
    }
    this.createOrUpdate(this.instancesApiService, value, true).subscribe((response: any) => {
      if (!this.authSvc.feature('utils.operations')) {
        this.notificationService.showMessage($localize`Create instance scheduled.`);
      } else {
        let responseMessage: string;
        if (response.number_of_instances && response.number_of_instances > 1) {
          responseMessage = $localize`Create instances operation started.`
        } else {
          responseMessage = $localize`Create instance operation started.`
        }
        if (response.operation_id) {
          this.notificationService.showMessageWithLink(
            responseMessage,
            `utilities/operations/${response.operation_id}`,
            $localize`See details`
          );
        } else {
          this.notificationService.showMessage(responseMessage);
        }
      }

      const redirectQueryParams = this.getRedirectQueryParamsAfterCreate(this.activatedRoute);
      this.redirectAfterCreate('openstack/instances', this.router, this.config, redirectQueryParams);
    });

    return of(null);
  }

  public refreshCreateOptions() {
    const clientId = (this.clientControl.value && this.clientControl.value.id) ? this.clientControl.value.id : null;
    const regionId = this.instanceForm.get('region').value;
    if (clientId) {
      const params = {
        client: clientId,
        region: regionId
      };
      if (this.imageLaunchId) {
        // @ts-ignore
        params.image = this.imageLaunchId;
      }
      this.instancesApiService.createOptions(params)
        .subscribe((createOptions: IInstanceCreateOptions) => {
          this.createOptions = createOptions;
          this.preselectPublicNics();
          if (this.imageLaunchId) {
            // init boot source on image launch
            const imagesArray = [
              this.createOptions.bootSources.image,
              this.createOptions.bootSources.owned_image,
              this.createOptions.bootSources.community_image,
            ];
            if (Array.isArray(this.createOptions.bootSources.requested_image)) {
              // add requested image array
              imagesArray.push(this.createOptions.bootSources.requested_image);
            }
            this.selectedBootSource = this.bootSourceSelectComponent.initBootSource(imagesArray);
            if (this.authSvc.feature('ui.openstack.instances.check_image_needs_sharing')) {
              this.checkImageAvailableToClient();
            }
          } else if (!this.setSimilarImage()) {
            // if function returns false, it means similar boot source image was not set, de-select boot source
            this.selectedBootSource = this.bootSourceSelectComponent.initBootSource();
          }
          this.setSimilarFlavor();
          this.switchAvailabilityZoneControl();
        });
    } else {
      // fetch only by region
      const params = {
        region: regionId
      };
      if (this.imageLaunchId) {
        // @ts-ignore
        params.image = this.imageLaunchId;
      }
      this.instancesApiService.createOptions(params).subscribe((createOptions: IInstanceCreateOptions) => {
        this.createOptions = createOptions;
        this.preselectPublicNics();
        if (!this.setSimilarImage()) {
          // if function returns false, it means similar boot source image was not set, de-select boot source
          this.selectedBootSource = this.bootSourceSelectComponent.initBootSource();
        }
        this.setSimilarFlavor();
        this.switchAvailabilityZoneControl();
      });
    }

    const selectedRegion = this.createOptions.region.find(region => region.id == regionId);
    if (selectedRegion.floating_ip_on_instance_create) {
      this.instanceForm.addControl('assign_floating_ip', this.formBuilder.control(false));
      this.instanceForm.controls.assign_floating_ip.valueChanges.subscribe(value => {
        if (value && this.canAssignFloatingIp) {
          if (!this.instanceForm.contains('floating_ip_network')) {
            this.instanceForm.addControl('floating_ip_network', this.formBuilder.control(null, Validators.required));
            this.setDefaultFloatingIpNetwork();
          }
        } else {
          if (this.instanceForm.contains('floating_ip_network')) {
            this.instanceForm.removeControl('floating_ip_network');
          }
        }
      });
    } else {
      this.instanceForm.removeControl('assign_floating_ip');
      this.canAssignFloatingIp = false;
      this.floatingIpNetworks = [];
    }
  }

  setDefaultFloatingIpNetwork() {
    this.createOptions.floating_nics.forEach(network => {
      for (const tag of network.tags) {
        if (tag.tag_name === 'floating-default') {
          this.instanceForm.controls.floating_ip_network.setValue(network.id);
          break;
        }
      }
    });
  }

  private checkImageAvailableToClient() {
    // if selected image boot source has to be shared with selected client, show a dialog to do so
    if (this.selectedBootSource && this.selectedBootSource.objectId) {
      const client = this.clientControl.value;
      this.imagesApiService.availableToClient(
        this.selectedBootSource.objectId,
        client.id
      ).subscribe({
        next: response => {
          if (response.needsSharing) {
            this.elementRef.nativeElement.ownerDocument.activeElement.blur();
            this.notificationService.confirmDialog({
              title: $localize`Share image to client?`,
              message: $localize`The selected image is not available to ${client.display}`,
              importantMessage: $localize`In order to proceed, the image needs to be shared with ${client.display}`,
            }).subscribe(dialogResult => {
              if (dialogResult?.button === 'yes') {
                this.shareImage = true;
              }
            });
          }
        }, error: () => {
          this.notificationService.showMessage($localize`Could not determine if image has to be shared with client.`);
        }
      });
    }
  }

  private preselectPublicNics() {
    this.instanceForm.controls.nics.setValue(null); // reset network selection first
    if (this.createOptions?.nics && this.createOptions.nics.length) {
      if (this.createOptions.nics.length === 1 && this.config.current.settings.instanceForm.hideNetworksIfOnlyOneAvailable) {
        // only one network, select it and hide input selector
        this.instanceForm.controls.nics.setValue([this.createOptions.nics[0].id]);
      } else if (this.config.current.settings.instanceForm.preselectPublicNetworks) {
        for (const nic of this.createOptions.nics) {
          for (const tag of nic.tags) {
            if (tag.tag_name === 'public') {
              this.instanceForm.controls.nics.setValue([nic.id]);
            }
          }
        }
      }
    }
  }

  public closeNicsSelect() {
    this.nicsSelect.close();
  }

  public switchUserData() {
    this.showUserData = !this.showUserData;
    if (this.showUserData === false) {
      this.instanceForm.controls.user_data.setValue(null);
    }
  }

  refreshFlavors(onlyCompatibleFlavors = false) {
    if (this.selectedBootSource && this.selectedBootSource.object) {
      this.instancesApiService.getFlavorsAssignedToImage(
        this.selectedBootSource.object.id, this.instanceForm.value.client?.id, onlyCompatibleFlavors
      ).subscribe({
        next: response => {
          this.createOptions.flavor = response.flavors;
          let oldSelectedStillPresent = false;
          const selectedFlavorId = this.instanceForm.controls.flavor.value;
          if (selectedFlavorId) {
            for (const flavor of this.createOptions.flavor) {
              if (flavor.id === selectedFlavorId) {
                oldSelectedStillPresent = true;
              }
            }
          }
          if (selectedFlavorId && !oldSelectedStillPresent) {
            this.instanceForm.controls.flavor.setValue('');
          }
        }, error: () => {
          this.notificationService.showMessage($localize`Could not refresh flavors.`);
        }
      });
    }
  };

  public onBootSourceChange(selectedBootSource: ISelectedBootSource) {
    this.selectedBootSource = selectedBootSource;
    if (this.bootSourceIsImage()) {
      if (!this.flavorsAsCards) {
        this.refreshFlavors();
      }
      // de-select selected flavor if it is incompatible
      if (this.instanceForm.controls.flavor.value) {
        for (const flavor of this.createOptions.flavor) {
          if (this.instanceForm.controls.flavor.value === flavor.id) {
            if (this.selectedBootSource.object.min_disk > flavor.root_gb ||
              this.selectedBootSource.object.min_ram > parseInt(flavor.memory_mb, 2)) {
              this.instanceForm.controls.flavor.setValue(null);
            }
          }
        }
      }
      if (this.selectedBootSource.object) {
        if (this.selectedBootSource.object?.properties &&
          this.selectedBootSource.object.properties.hasOwnProperty('fleio_default_image_username')) {
          const properties = this.selectedBootSource.object.properties as any;
          const username = properties.fleio_default_image_username;
          this.passwordLabel = $localize`Password`;
          this.passwordHint = $localize`Password will be set for user ${username}`;
          this.sshKeysHint = $localize`Keys will be set for user ${username}`;
        } else {
          this.setAuthInfoText();
        }

        this.showPasswordInput = !(this.selectedBootSource.object?.properties &&
          this.selectedBootSource.object.properties.hasOwnProperty('fleio_do_not_ask_password'));
      }
    } else {
      this.setAuthInfoText();
    }
    this.getSimulatedPrice();
  }

  setAuthInfoText() {
    this.passwordLabel = $localize`Root password`;
    this.passwordHint = null;
    this.sshKeysHint = null;
  }

  public bootSourceIsWindows() {
    return (this.selectedBootSource && this.selectedBootSource.object &&
      this.selectedBootSource.object.os_distro === 'windows');
  }

  public bootSourceIsImage() {
    if (this.selectedBootSource && this.selectedBootSource.type) {
      return [
        'image', 'owned_image', 'community_image', 'shared_image', 'requested_image'
      ].indexOf(this.selectedBootSource.type) > -1;
    }
    return false;
  }

  private setSimilarFlavor() {
    // on region change, select a similar flavor
    if (this.createOptions.flavor && this.instanceForm.controls.flavor.value) {
      if (this.selectedFlavor && this.selectedFlavor.name && this.selectedFlavor.memory_mb) {
        for (const newFlavor of this.createOptions.flavor) {
          if (newFlavor.name === this.selectedFlavor.name && newFlavor.memory_mb === this.selectedFlavor.memory_mb) {
            if (!newFlavor.out_of_stock) {
              this.instanceForm.controls.flavor.setValue(newFlavor.id);
              this.flavorChanged(newFlavor);
              return;
            } else {
              break;
            }
          }
        }
      }
    }
    // de-select flavor if a similar one was not found
    this.instanceForm.controls.flavor.setValue(null);
    this.flavorChanged(null);
  }

  onFlavorChosen(flavor: IFlavorModel) {
    // on flavors as cards flavor select
    this.flavorChanged(flavor);
  }

  flavorChanged(newFlavor: IFlavorModel) {
    if (newFlavor) {
      // this will trigger flavor - boot source compatibility checks in boot source component
      this.selectedFlavor = newFlavor;
      if (this.flavorsAsCards) {
        this.flavorsAsCardsComponent.selectedFlavor = newFlavor;
      }
      // ensure flavor is set on form
      this.instanceForm.controls.flavor.setValue(newFlavor.id);
    } else {
      if (this.flavorsAsCards) {
        this.flavorsAsCardsComponent.selectedFlavor = null;
      }
    }
    if (this.flavorsAsCards) {
      this.refreshImages(newFlavor);
    }
  }

  refreshImages(newFlavor: IFlavorModel) {
    this.instancesApiService.getFlavorRelatedImages(
      newFlavor,
      this.instanceForm.controls.region.value,
      (this.clientControl.value && this.clientControl.value.id) ? this.clientControl.value.id : null,
    ).subscribe(response => {
      if (response.bootSources) {
        this.createOptions.bootSources.image = response.bootSources.image;
        this.createOptions.bootSources.owned_image = response.bootSources.owned_image;
        this.createOptions.bootSources.shared_image = response.bootSources.shared_image;
        this.createOptions.bootSources.community_image = response.bootSources.community_image;
      }
      this.checkAndResetBootSources(newFlavor);
    }, () => {
      this.notificationService.showMessage($localize`Failed to load images related to selected flavor.`);
    });
  }

  isFlavorDisabled(flavor) {
    // If booting from image , test image disk size to make sure we can boot from it
    if (this.selectedBootSource && this.selectedBootSource.object &&
      ['image', 'owned_image', 'community_image'].indexOf(this.selectedBootSource.type) > -1) {
      return !flavor || (this.selectedBootSource.object.min_disk > flavor.root_gb && flavor.root_gb > 0) ||
        this.selectedBootSource.object.min_ram > flavor.memory_mb;
    }
    return false;
  }

  checkOldImageAvailableInNewImages(image, alreadyFound) {
    if (alreadyFound === true) {
      return true;
    }
    if (this.selectedBootSource && ['image', 'owned_image', 'shared_image',
        'community_image'].indexOf(this.selectedBootSource.type) > -1 && this.selectedBootSource.object &&
      this.selectedBootSource.object.id === image.id) {
      return image.assigned_to_flavor;
    }
    return false;
  }

  checkAndResetBootSources(newFlavor: IFlavorModel) {
    let maxRam = Number.MAX_VALUE;
    let maxStorage = Number.MAX_VALUE;
    if (newFlavor) {
      maxRam = newFlavor.memory_gb;
      maxStorage = newFlavor.root_gb;
    }
    const imagesKeys = ['image', 'owned_image', 'shared_image', 'community_image'];
    let availableInNewImages = false;
    for (const imageKey of imagesKeys) {
      for (const image of this.createOptions.bootSources[imageKey]) {
        image.disabled = (image.min_disk > maxStorage && maxStorage > 0) || ((image.min_ram / 1024) > maxRam);
        availableInNewImages = this.checkOldImageAvailableInNewImages(image, availableInNewImages);
      }
    }
    if (this.isFlavorDisabled(newFlavor) || !availableInNewImages) {
      this.selectedBootSource = this.bootSourceSelectComponent.initBootSource();
    }
  }

  private setSimilarImage() {
    if (this.selectedBootSource && this.selectedBootSource.object && this.selectedBootSource.type === 'image'
      && this.selectedBootSource.createNewVolume) {
      let foundSimilarVolumeType = false;
      for (const volumeType of this.createOptions.bootSources.volume_types) {
        if (volumeType.name === this.selectedBootSource.storageType) {
          foundSimilarVolumeType = true;
        }
      }
      if (!foundSimilarVolumeType) {
        return false;
      }
    }
    if (this.selectedBootSource && this.selectedBootSource.object && this.selectedBootSource.type === 'image'
      && this.createOptions.bootSources.image && this.createOptions.bootSources.image.length) {
      const filteredImages = this.createOptions.bootSources.image.filter(img =>
        img.min_disk <= this.selectedBootSource.object.min_disk &&
        img.os_distro === this.selectedBootSource.object.os_distro &&
        img.os_version === this.selectedBootSource.object.os_version &&
        img.architecture === this.selectedBootSource.object.architecture &&
        img.min_ram <= this.selectedBootSource.object.min_ram
      );
      let newImage = filteredImages.find(
        img => img.name.toLowerCase() === this.selectedBootSource.object.name.toLowerCase()
      );
      if (!newImage && filteredImages.length) {
        newImage = filteredImages[0];
      }
      if (newImage) {
        // similar image was found
        this.selectedBootSource.object = newImage as any;
        this.selectedBootSource.objectId = newImage.id as string;
        this.bootSourceSelectComponent.selectedBootSource.object = newImage as any;
        this.bootSourceSelectComponent.selectedBootSource.objectId = newImage.id as string;
        this.bootSourceSelectComponent.similarBootSourceWasSet();
        return true;
      }
    }
    return false;
  }

  public getSelectedFlavor(): (IFlavorModel | null) {
    if (this.flavorsAsCards) {
      return this.selectedFlavor;
    }
    if (this.instanceForm.controls.flavor.value) {
      for (const flavor of this.createOptions.flavor) {
        if (flavor.id === this.instanceForm.controls.flavor.value) {
          return flavor;
        }
      }
    }
    return null;
  }

  switchAvailabilityZoneControl() {
    if (this.createOptions?.availability_zones?.length && this.config.authService.feature('openstack.instances.availability_zones')) {
      if (!this.instanceForm.contains('availability_zone')) {
        this.instanceForm.addControl('availability_zone', this.formBuilder.control(''));
      }
      if (this.createOptions.availability_zones.length === 1 && !this.instanceForm.controls.availability_zone.value) {
        this.instanceForm.controls.availability_zone.setValue(this.createOptions.availability_zones[0]);
      }
    } else {
      if (this.instanceForm.contains('availability_zone')) {
        this.instanceForm.removeControl('availability_zone');
      }
    }
  }

  ngOnInit() {
    if (this.objectController) {
      this.objectController.actionCallback = () => this.saveInstance();
    }

    if (this.clientLaunchId && this.authSvc.feature('ui.openstack.instances.select-client')) {
      // in enduser, clientLaunchId should not be used as launch is done on active client
      this.clientApiService.get(this.clientLaunchId).subscribe(value => {
        this.instanceForm.controls.client.setValue(value);
      });
    }

    if (this.authSvc.feature('openstack.instances.create-multiple')) {
      this.instanceForm.addControl(
        'number_of_instances',
        this.formBuilder.control(1, [
          ValidatorsHelper.requiredIntegerValidator, ValidatorsHelper.nonZeroValidator
        ])
      );
    }

    if (this.objectController && this.objectController.additionalObjects &&
      this.objectController.additionalObjects.clients) {
      this.clients = this.objectController.additionalObjects.clients;
    }

    if (this.createOptions) {
      if (this.activatedRoute.snapshot.queryParams.region) {
        this.createOptions.selected_region = this.activatedRoute.snapshot.queryParams.region;
      }
      for (const region of this.createOptions.region) {
        if (this.createOptions.selected_region && region.id === this.createOptions.selected_region) {
          this.instanceForm.get('region').setValue(region.id);
        }
      }
      this.switchAvailabilityZoneControl();

      this.clientControl.valueChanges.pipe(
        startWith(null), pairwise(),
      ).subscribe(([prev, next]) => {
        if (next && next.id != prev?.id) {
          // we have a value changed from previous value, refresh create options
          this.refreshCreateOptions();
        }
      });
    }

    this.instanceForm.controls.nics.valueChanges.subscribe(value => {
      this.canAssignFloatingIp = false;
      if (value && Array.isArray(value)) {
        value.forEach(networkId => {
          const network = this.createOptions.nics.find(
            network => network.id === networkId
          );
          if (!network.router_external) {
            this.canAssignFloatingIp = true;
          }
        });
      }
      if (this.canAssignFloatingIp) {
        this.instancesApiService.getFloatingIPNetworks(value, this.instanceForm.controls.region.value).subscribe(response => {
          this.floatingIpNetworks = response.objects;
        });
      }
    });

    this.preselectPublicNics();

    if (!this.selectClient) {
      if (this.createOptions.client) {
        this.clientControl.setValue(this.createOptions.client);
      }
    }

    this.selectedFlavor = null;
    this.instanceForm.controls.flavor.valueChanges.subscribe(flavorId => {
      this.selectedFlavor = this.createOptions.flavor.find(flavor => flavor.id === flavorId);
    });

    this.instanceForm.valueChanges.pipe(debounceTime(300)).subscribe(() => {
      this.getSimulatedPrice();
    });
  }

  addSSHKey() {
    let keysList: Array<FleioId> = this.instanceForm.controls.ssh_keys.value;
    keysList = keysList.filter(key => key !== undefined);
    this.instanceForm.controls.ssh_keys.setValue(keysList);

    this.matDialog.open(
      SshKeyAddDialogComponent, {
        data: {
          client_id: this.instanceForm.controls.client.value?.id,
        },
        autoFocus: false,
      }).afterClosed().subscribe((publicKey) => {
      if (publicKey && publicKey.id) {
        this.createOptions.ssh_key.push(publicKey);
        let keysList: Array<FleioId> = this.instanceForm.controls.ssh_keys.value;
        keysList.push(publicKey.id);
        this.instanceForm.controls.ssh_keys.setValue(keysList);
      }
    });
  }

  getSimulatedPrice() {
    if (this.authSvc.feature('openstack.instances.show-price-on-create')) {
      const clientId = this.instanceForm.controls.client.value?.id;
      const region = this.instanceForm.controls.region.value;
      const flavorId = this.selectedFlavor?.id || this.instanceForm.controls.flavor.value;
      const imageId = this.bootSourceIsImage() ? this.selectedBootSource.objectId : null;
      const networkIds = this.instanceForm.controls.nics.value;
      let numberOfInstances = 1;
      if (this.authSvc.feature('openstack.instances.create-multiple')) {
        numberOfInstances = this.instanceForm.controls.number_of_instances.value
      }
      let volumeSize = null;
      let volumeType = null;
      if (this.selectedBootSource && this.selectedBootSource.createNewVolume) {
        volumeSize = this.selectedBootSource.volumeSize;
        volumeType = this.selectedBootSource.storageType;
      }
      if (clientId && region && flavorId && imageId && (networkIds && Array.isArray(networkIds) && networkIds.length > 0)) {
        const params = {
          client_id: clientId,
          region,
          flavor_id: flavorId,
          network_ids: networkIds,
          number_of_instances: numberOfInstances,
        } as any;
        if (this.instanceForm.contains('availability_zone')) {
          params.availability_zone = this.instanceForm.controls.availability_zone.value;
        }
        if (this.instanceForm.contains('floating_ip_network')) {
          params.floating_ip_network = this.instanceForm.controls.floating_ip_network.value || null;
        }
        if (imageId) {
          params.image_id = imageId;
        }
        if (volumeSize && volumeType) {
          params.volume_size = volumeSize;
          params.volume_type = volumeType;
        }
        this.estimatedCostLoading = true;
        this.instancesApiService.getPrice(params).subscribe({
          next: response => {
            this.estimatedCost = response.estimated_cost_display;
          },
          error: error => {
            if (error?.error?.detail) {
              if (this.config.authService.feature('ui.staff')) {
                this.estimatedCost = error.error.detail;
              } else {
                this.estimatedCost = null;
              }
            }
          }
        }).add(() => {
          this.estimatedCostLoading = false;
        });
      } else {
        this.estimatedCost = null;
      }
    }
  }
}
