import { Component, OnInit } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { IClusterModel } from '@fleio-api/openstack/model/cluster.model';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { ClustersApiService } from '@fleio-api/openstack/cluster/clusters-api.service';
import { Router } from '@angular/router';
import { ConfigService } from '@shared/config/config.service';
import { Observable, of } from 'rxjs';
import { IClusterTemplateModel } from '@fleio-api/openstack/model/cluster-template.model';
import { debounceTime } from 'rxjs/operators';
import { ClusterTemplatesApiService } from '@fleio-api/openstack/cluster-template/cluster-templates-api.service';
import { IClusterCreateOptionsModel } from '@fleio-api/openstack/model/cluster-create-options.model';
import { NotificationService } from '@shared/ui-api/notification.service';
import { ApiUsersApiService } from '@fleio-api/openstack/api-user/api-users-api.service';
import { IApiUserModel } from '@fleio-api/openstack/model/api-user.model';
import { IClientModel } from '@fleio-api/client-user/model/client.model';
import { FleioId } from '@fleio-api/base-model/base-fleio-object.model';
import { ValidatorsHelper } from '@shared/ui-api/helpers/validators-helper';

@Component({
  selector: 'app-cluster-create-form',
  templateUrl: './cluster-create-form.component.html',
  styleUrls: ['./cluster-create-form.component.scss']
})
export class ClusterCreateFormComponent extends DetailsFormBase<IClusterModel> implements OnInit {
  createClusterForm = this.formBuilder.group({
    name: ['', Validators.required],
    cluster_template: ['', Validators.required],
    master_count: [1, [ValidatorsHelper.requiredIntegerValidator, Validators.min(1)]],
    node_count: [1, [ValidatorsHelper.requiredIntegerValidator, Validators.min(1)]],
    docker_volume_size: [null, [ValidatorsHelper.requiredIntegerValidator, Validators.min(1)]],
    keypair: ['', Validators.required],
    flavor_id: ['', Validators.required],
    master_flavor_id: ['', Validators.required],
    floating_ip_enabled: [false],
    labels: [''],
    auto_generate_os_api_user: [true],
  });
  createOptions: IClusterCreateOptionsModel;
  clusterTemplate = this.createClusterForm.controls.cluster_template;
  dockerVolumeSizeRequired = true;
  dockerVolumeSizeMin = 1;
  chooseApiUser = false;
  apiUsers: Array<IApiUserModel> = [];
  clientSelection: boolean;
  createInClientProject = true;
  floatingIpEnabledTooltip = $localize`Assigns floating IPs to all components`;
  masterLBFloatingIpEnabledTooltip = $localize`If checked, OpenStack will assign floating IPs only to load balancers`;
  clusterTemplateSelectedPlaceholder = $localize`Cluster template`;
  clusterTemplateUnselectedPlaceholder = $localize`Select cluster template ...`;

  estimatedCost: string;
  estimatedCostLoading = false;
  // labels take slightly longer to update thus we should not get price until they are set
  labelsFinishedUpdateOnTemplateChange = true;
  private latestPriceParams = null;

  constructor(
    private formBuilder: UntypedFormBuilder,
    private clustersApiService: ClustersApiService,
    public clusterTemplatesApiService: ClusterTemplatesApiService,
    private notificationService: NotificationService,
    private router: Router,
    public config: ConfigService,
    private apiUsersApiService: ApiUsersApiService,
  ) {
    super();
  }

  changeDestination(value) {
    // createInClientProject
    if (value === false) {
      this.createClusterForm.controls.client.setValue('');
      this.createClusterForm.controls.client.clearValidators();
      this.createClusterForm.controls.client.reset();
    } else {
      this.createClusterForm.controls.client.setValidators(Validators.required);
    }
  }

  matchAndPreselectFlavor(fieldName: string, template: IClusterTemplateModel) {
    // sometimes flavor on template is a string representing a name and we need to treat this
    // if we cannot match by id
    let flavorIdMatched = false;
    let matchedFlavorByName = null;
    for (const flavor of this.createOptions.flavors) {
      if (flavor.id === template[fieldName]) {
        flavorIdMatched = true;
        this.createClusterForm.controls[fieldName].setValue(template[fieldName]);
        break;
      }
      if (flavor.name === template[fieldName]) {
        matchedFlavorByName = flavor;
      }
    }
    if (!flavorIdMatched && matchedFlavorByName) {
      this.createClusterForm.controls[fieldName].setValue(matchedFlavorByName.id);
    }
  }

  formatLabelsToStr(labels) {
    let asString = '';
    for (const key in labels) {
      if (labels.hasOwnProperty(key)) {
        asString = asString + key + '=' + labels[key] + ',';
      }
    }
    return asString.substring(0, asString.length - 1); // remove last ',' as it is redundant
  };

  getCreateOptions(clusterTemplateId: FleioId, clientId?: FleioId): Observable<IClusterCreateOptionsModel> {
    const params: {
      cluster_template_id: FleioId;
      client_id?: FleioId;
    } = {
      cluster_template_id: clusterTemplateId,
    };

    if (clientId) {
      params.client_id = clientId;
    }

    return this.clustersApiService.createOptions(params) as Observable<IClusterCreateOptionsModel>;
  }

  changedTemplate() {
    if (this.clusterTemplate.value) {
      const clusterTemplateId = this.clusterTemplate.value.id;
      let clientId = null;
      if (this.clientSelection) {
        clientId = this.createClusterForm.controls.client.value?.id;
      }
      this.getCreateOptions(clusterTemplateId, clientId)
        .subscribe((createOptions: IClusterCreateOptionsModel) => {
          this.setCreateOptions(createOptions);
          this.clusterTemplatesApiService.get(clusterTemplateId)
            .subscribe((template: IClusterTemplateModel) => {
              this.labelsFinishedUpdateOnTemplateChange = false;
              if (template.docker_volume_size) {
                this.createClusterForm.controls.docker_volume_size.setValue(template.docker_volume_size);
              } else {
                this.createClusterForm.controls.docker_volume_size.setValue(null);
              }
              if (template.master_flavor_id) {
                this.matchAndPreselectFlavor('master_flavor_id', template);
              }
              if (template.flavor_id) {
                this.matchAndPreselectFlavor('flavor_id', template);
              }
              if (template.labels) {
                const labelsToStr = this.formatLabelsToStr(
                  JSON.parse(template.labels.replace(/'/g, '"'))
                );
                this.createClusterForm.controls.labels.setValue(labelsToStr);
              } else {
                this.createClusterForm.controls.labels.setValue('');
              }
              this.labelsFinishedUpdateOnTemplateChange = true;
              if (template.keypair_id) {
                let templateKeyInCreateOptions = false;
                for (const keyPair of this.createOptions.keypairs) {
                  if (keyPair.name === template.keypair_id) {
                    templateKeyInCreateOptions = true;
                  }
                }
                if (templateKeyInCreateOptions) {
                  this.createClusterForm.controls.keypair.setValue(template.keypair_id);
                }
              }
              this.createClusterForm.controls.floating_ip_enabled.setValue(template.floating_ip_enabled);
              if (this.createClusterForm.contains('master_lb_enabled')) {
                this.createClusterForm.controls.master_lb_enabled.setValue(template.master_lb_enabled);
              }

              if (!template.hide_discovery_url) {
                if (!this.createClusterForm.contains('discovery_url')) {
                  this.createClusterForm.addControl('discovery_url', this.formBuilder.control(''));
                }
              } else {
                if (this.createClusterForm.contains('discovery_url')) {
                  this.createClusterForm.removeControl('discovery_url');
                }
              }

              if (!template.hide_timeout_input) {
                if (!this.createClusterForm.contains('create_timeout')) {
                  this.createClusterForm.addControl('create_timeout', this.formBuilder.control(6000));
                }
              } else {
                if (this.createClusterForm.contains('create_timeout')) {
                  this.createClusterForm.removeControl('create_timeout');
                }
              }

              if (template.make_labels_immutable) {
                this.createClusterForm.controls.labels.disable();
              } else {
                if (this.createClusterForm.controls.labels.disabled) {
                  this.createClusterForm.controls.labels.enable();
                }
              }

              this.updateDockerVolumeSizeConditions(template.docker_storage_driver);

            }, () => {
              this.notificationService.showMessage($localize`Could not retrieve selected cluster template details.`);
            });
        }, () => {
          this.notificationService.showMessage($localize`Could not retrieve create options for cluster template.`);
        });
    } else {
      this.createClusterForm.controls.flavor_id.setValue(null);
      this.createClusterForm.controls.master_flavor_id.setValue(null);
      this.createClusterForm.controls.labels.setValue('');
      this.createClusterForm.controls.keypair.setValue(null);
    }
  }

  updateDockerVolumeSizeConditions(dockerStorageDriver) {
    if (dockerStorageDriver === 'devicemapper') {
      this.dockerVolumeSizeRequired = true;
      this.dockerVolumeSizeMin = 3;
      this.createClusterForm.controls.docker_volume_size.setValidators(
        [ValidatorsHelper.requiredIntegerValidator, Validators.min(this.dockerVolumeSizeMin)]
      );
    } else {
      this.dockerVolumeSizeRequired = false;
      this.dockerVolumeSizeMin = 1;
      this.createClusterForm.controls.docker_volume_size.setValidators(
        [Validators.min(this.dockerVolumeSizeMin)]
      );
    }
    const dockerVolumeSize = this.createClusterForm.controls.docker_volume_size;
    if (dockerVolumeSize.value && dockerVolumeSize.value < this.dockerVolumeSizeMin) {
      dockerVolumeSize.setValue(null);
    }
  }

  createCluster() {
    const value = this.createClusterForm.getRawValue() as any;
    if (value.cluster_template) {
      value.cluster_template_id = value.cluster_template.id;
      delete value.cluster_template;
    }
    if (this.clientSelection && value.client && (value.client as IClientModel).id) {
      value.client = (value.client as IClientModel).id;
    }
    if (!this.object.id && !this.createInClientProject) {
      value.create_as_admin = true;
    }

    this.createOrUpdate(this.clustersApiService, value, true).subscribe(() => {
      this.redirectAfterCreate('openstack/clusters', this.router, this.config);
    });
    return of(null);
  }

  ngOnInit(): void {
    super.ngOnInit();
    if (this.objectController) {
      this.objectController.actionCallback = () => this.createCluster();
      this.createOptions = this.objectController.additionalObjects.createOptions;
    }
    this.clientSelection = this.config.authService.feature('ui.openstack.clusters.create.select-client');
    if (this.authSvc.feature('ui.openstack.clusters.create.discovery_url_and_timeout')) {
      this.createClusterForm.addControl('discovery_url', this.formBuilder.control(''));
      // create_timeout: [60, [Validators.required, Validators.min(0)]],
      this.createClusterForm.addControl('create_timeout', this.formBuilder.control(
        60, [ValidatorsHelper.requiredIntegerValidator, Validators.min(0)])
      );
    }
    if (this.clientSelection) {
      this.createClusterForm.addControl('client', this.formBuilder.control('', Validators.required));
      this.createClusterForm.controls.client.valueChanges.subscribe((newClient) => {
        if (this.clusterTemplate.value && (typeof newClient !== 'string')) {
          const clientId = newClient?.id || undefined;
          const clusterTemplateId = this.clusterTemplate.value.id;
          this.getCreateOptions(clusterTemplateId, clientId)
            .subscribe((createOptions: IClusterCreateOptionsModel) => {
              this.setCreateOptions(createOptions);
            });
        }
      });
    }

    this.clusterTemplate.valueChanges.subscribe(value => {
      if (value && value.id) {
        this.changedTemplate();
      } else {
        this.estimatedCost = null;
      }
    });

    this.createClusterForm.controls.auto_generate_os_api_user.valueChanges.subscribe(value => {
      if (value === false) {
        this.chooseApiUser = true;
        this.apiUsersApiService.list().subscribe(response => {
          this.apiUsers = response.objects;
        });
        this.createClusterForm.addControl(
          'os_api_user_id', this.formBuilder.control(null, Validators.required)
        );
        this.createClusterForm.addControl(
          'os_api_user_password', this.formBuilder.control(null, Validators.required)
        );
      } else if (value === true) {
        this.chooseApiUser = false;
        if (this.createClusterForm.contains('os_api_user_id')) {
          this.createClusterForm.removeControl('os_api_user_id');
        }
        if (this.createClusterForm.contains('os_api_user_password')) {
          this.createClusterForm.removeControl('os_api_user_password');
        }
      }
    });

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

  }

  getSimulatedPrice() {
    if (this.authSvc.feature('openstack.coe.clusters.create.show_price')) {
      const clusterTemplate = this.createClusterForm.controls.cluster_template.value as IClusterTemplateModel;
      if (clusterTemplate && clusterTemplate.id && this.labelsFinishedUpdateOnTemplateChange === true) {
        const region = clusterTemplate.region;
        const imageId = clusterTemplate.image_id;
        const nodeFlavor = this.createClusterForm.controls.flavor_id.value;
        const nodeCount = this.createClusterForm.controls.node_count.value;
        const masterFlavor = this.createClusterForm.controls.master_flavor_id.value;
        const masterCount = this.createClusterForm.controls.master_count.value;
        const dockerVolumeSize = this.createClusterForm.controls.docker_volume_size.value;
        const floatingIpEnabled = this.createClusterForm.controls.floating_ip_enabled.value;
        const labels = this.createClusterForm.controls.labels.value;
        let clientId = null;
        if (this.clientSelection) {
          clientId = this.createClusterForm.controls.client.value?.id;
        }
        if (nodeFlavor && nodeCount && masterFlavor && masterCount &&
          ((this.clientSelection && clientId) || !this.clientSelection)) {
          const params = {
            node_flavor: nodeFlavor,
            node_count: nodeCount,
            master_flavor: masterFlavor,
            master_count: masterCount,
            docker_volume_size: dockerVolumeSize ? dockerVolumeSize : 0,
            image_id: imageId,
            floating_ip_enabled: floatingIpEnabled,
            labels,
            region,
          } as any;
          if (clientId) {
            params.client_id = clientId;
          }
          if (!this.latestPriceParams) {
            this.latestPriceParams = JSON.parse(JSON.stringify(params));
          } else {
            if (JSON.stringify(this.latestPriceParams) === JSON.stringify(params) && this.estimatedCost) {
              // same params as last request, do nothing
              return;
            } else {
              // continue to make the request, re-set latest params
              this.latestPriceParams = JSON.parse(JSON.stringify(params));
            }
          }
          this.estimatedCostLoading = true;
          this.clustersApiService.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;
        }
      }
    }
  }

  private setCreateOptions(createOptions: IClusterCreateOptionsModel) {
    this.createOptions = createOptions;
    if (this.createOptions.master_lb_flag) {
      if (!this.createClusterForm.contains('master_lb_enabled')) {
        this.createClusterForm.addControl('master_lb_enabled', this.formBuilder.control(false));
      }
      this.createClusterForm.addControl(
        'master_lb_floating_ip_enabled', this.formBuilder.control(false)
      );
    } else {
      if (this.createClusterForm.contains('master_lb_enabled')) {
        this.createClusterForm.removeControl('master_lb_enabled');
      }
    }

  }
}
