import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { ILoadBalancerModel } from '@fleio-api/openstack/load-balancing/model/load-balancer.model';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { LoadBalancersApiService } from '@fleio-api/openstack/load-balancing/load-balancers/load-balancers-api.service';
import { Router } from '@angular/router';
import { ConfigService } from '@shared/config/config.service';
import { ClipboardService } from '@shared/ui-api/clipboard.service';
import { Observable, of } from 'rxjs';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { IAction } from '@objects-view/interfaces/actions/action';
import { ILBCreateOptionsModel } from '@fleio-api/openstack/load-balancing/model/lb-create-options.model';
import { InstancesApiService } from '@fleio-api/openstack/instance/instances-api.service';
import { IInstanceModel } from '@fleio-api/openstack/model/instance.model';
import { debounceTime, map, mergeMap, startWith } from 'rxjs/operators';
import { IStepDetails } from '../../step-details';
import { MonitorFormHelper } from '../../lb-pools/tabs/monitor-form-helper';
import { NotificationService } from '@shared/ui-api/notification.service';

@Component({
  selector: 'app-load-balancer-create-form',
  templateUrl: './load-balancer-create-form.component.html',
  styleUrls: ['./load-balancer-create-form.component.scss']
})
export class LoadBalancerCreateFormComponent extends DetailsFormBase<ILoadBalancerModel> implements OnInit {
  loadBalancerDetails = this.formBuilder.group({
    name: ['',],
    vip_address: [''],
    description: [''],
    availability_zone: [''],
    flavor_id: [null],
    vip_subnet_id: ['', Validators.required],
    admin_state_up: [true],
  });
  listenerDetails = this.formBuilder.group({
    name: ['',],
    description: [''],
    protocol: ['', Validators.required],
    protocol_port: [null, [Validators.min(1), Validators.max(65535)]],
    timeout_client_data: [50000, Validators.min(0)],
    timeout_tcp_inspect: [0, Validators.min(0)],
    timeout_member_connect: [5000, Validators.min(0)],
    timeout_member_data: [50000, Validators.min(0)],
    connection_limit: [-1, Validators.min(-1)],
    x_forwarded_for: [false],
    x_forwarded_port: [false],
    x_forwarded_proto: [false],
    x_ssl_client_verify: [false],
    x_ssl_client_has_cert: [false],
    x_ssl_client_dn: [false],
    x_ssl_client_cn: [false],
    x_ssl_issuer: [false],
    x_ssl_client_sha1: [false],
    x_ssl_client_not_before: [false],
    x_ssl_client_not_after: [false],
    admin_state_up: [true],
    default_tls_container_ref: [null, Validators.required],
    tls_ciphers: [],
  });
  sessionPersistence = this.formBuilder.group({
    type: [''],
    cookie_name: ['', Validators.required],
  });
  poolDetails = this.formBuilder.group({
    name: ['', Validators.required],
    description: [''],
    lb_algorithm: ['', Validators.required],
    session_persistence: this.sessionPersistence,
    admin_state_up: [true],
  });
  poolMembers = this.formBuilder.array([]);
  monitorDetails = new MonitorFormHelper(this.formBuilder).getMonitorForm();
  loadBalancerForm = this.formBuilder.group({
    load_balancer_details: this.loadBalancerDetails,
    listener_details: this.listenerDetails,
    pool_details: this.poolDetails,
    pool_members: this.poolMembers,
    monitor_details: this.monitorDetails,
  });
  searchInstances = this.formBuilder.group({
    search: [''],
  });

  estimatedCost: string;
  estimatedCostLoading = false;

  steps: { [stepNo: number]: IStepDetails } = {
    1: {
      name: $localize`Load balancer details`,
      valid: false,
      selected: true,
      formControl: this.loadBalancerDetails,
    },
    2: {
      name: $localize`Listener details`,
      valid: false,
      selected: false,
      formControl: this.listenerDetails,
    },
    3: {
      name: $localize`Pool details`,
      valid: false,
      selected: false,
      formControl: this.poolDetails,
    },
    4: {
      name: $localize`Pool members`,
      valid: true,
      selected: false,
      formControl: this.poolMembers,
    },
    5: {
      name: $localize`Monitor details`,
      valid: false,
      selected: false,
      formControl: this.monitorDetails,
    },
  };
  activeStep = 1;
  createOptions: ILBCreateOptionsModel;
  flavorPlaceholder = $localize`Flavor`;
  filteredInstances$: Observable<IInstanceModel[]>;

  constructor(
    private formBuilder: UntypedFormBuilder,
    private loadBalancersApiService: LoadBalancersApiService,
    private router: Router,
    public config: ConfigService,
    public clipboard: ClipboardService,
    private instancesApiService: InstancesApiService,
    private notificationService: NotificationService,
    protected changeDetectorRef: ChangeDetectorRef,
  ) {
    super();
  }

  ngOnInit() {
    super.ngOnInit();
    if (this.objectController) {
      this.objectController.actionCallback = (action: IAction) => this.loadBalancerAction(action);
      if (this.objectController.additionalObjects) {
        this.createOptions = this.objectController.additionalObjects.createOptions;
        if (!this.createOptions.flavors?.length) {
          this.loadBalancerDetails.controls.flavor_id.disable();
          this.flavorPlaceholder = $localize`No flavors available`;
        }
      }
      this.updateActions();
    }

    this.loadBalancerForm.valueChanges.subscribe(() => {
      this.steps[this.activeStep].valid = this.steps[this.activeStep].formControl.valid;
      this.updateCreateAction(this.primaryAction);
    });

    this.listenerDetails.controls.protocol.valueChanges.subscribe(value => {
      if (value === 'HTTP' && !this.listenerDetails.controls.protocol_port.value) {
        this.listenerDetails.controls.protocol_port.setValue(80);
      }
      if (['HTTPS', 'TERMINATED_HTTPS'].includes(value) && !this.listenerDetails.controls.protocol_port.value) {
        this.listenerDetails.controls.protocol_port.setValue(443);
      }
      if (value === 'TERMINATED_HTTPS') {
        this.listenerDetails.controls.default_tls_container_ref.enable();
      } else {
        this.listenerDetails.controls.default_tls_container_ref.disable();
      }
    });

    this.sessionPersistence.controls.cookie_name.disable({emitEvent: false, onlySelf: true});
    this.sessionPersistence.controls.type.valueChanges.subscribe((value) => {
      if (value !== 'APP_COOKIE') {
        this.sessionPersistence.controls.cookie_name.disable({emitEvent: false, onlySelf: true});
      } else {
        this.sessionPersistence.controls.cookie_name.enable({emitEvent: false, onlySelf: true});
      }
    });

    this.filteredInstances$ = this.searchInstances.controls.search.valueChanges.pipe(
      startWith(''),
      debounceTime(300),
      mergeMap(value => this.instancesApiService.list({
        search: value,
        openstack_project: true,
      }).pipe(map(instances => instances.objects)))
    );

    this.getSimulatedPrice();
    this.loadBalancerDetails.controls.flavor_id.valueChanges.pipe(debounceTime(300)).subscribe(() => {
      this.getSimulatedPrice();
    });
  }

  updateActions() {
    for (const action of this.detailsActions) {
      switch (action.internalName) {
        case 'back':
          action.disabled = this.activeStep <= 1;
          break;
        case 'next':
          action.disabled = this.activeStep >= 5;
          break;
        case 'create':
          this.updateCreateAction(action);
          break;
      }
    }
  }

  updateCreateAction(action: IAction) {
    action.disabled = !this.loadBalancerForm.valid;
  }

  stepChanged(stepNoStr: any) {
    const stepNo = parseInt(stepNoStr, 10);
    this.steps[this.activeStep].selected = false;
    this.activeStep = stepNo;
    this.steps[this.activeStep].selected = true;
    this.updateActions();
  }

  addExternalPoolMember() {
    this.poolMembers.push(this.formBuilder.group({
      address: ['', Validators.required],
      protocol_port: [null, [Validators.required, Validators.min(1), Validators.max(65535)]],
      subnet_id: [''],
      weight: [1, [Validators.required, Validators.min(1)]],
    }));
  }

  removePoolMember(index: number) {
    this.poolMembers.removeAt(index);
  }

  addExistingInstance(instance: IInstanceModel) {
    const subnetId = this.createOptions.subnets.find(s => s.id === instance.access_subnet_id)?.id || '';
    this.poolMembers.push(this.formBuilder.group({
      address: [instance.access_ip, Validators.required],
      protocol_port: [null, [Validators.required, Validators.min(1), Validators.max(65535)]],
      subnet_id: [subnetId],
      weight: [1, [Validators.required, Validators.min(1)]],
    }));
  }

  getSimulatedPrice() {
    if (this.authSvc.feature('openstack.lbaas.show-price-on-create')) {
      this.estimatedCostLoading = true;
      const lb = this.loadBalancerDetails.value;
      const params = {} as any;
      if (lb.flavor_id) {
        params.flavor_id = lb.flavor_id;
      }
      this.loadBalancersApiService.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;
      });
    }
  }

  loadBalancerAction(action: IAction): Observable<IActionResult> {
    switch (action.internalName) {
      case 'back':
        this.stepChanged(this.activeStep - 1);
        return of(null);
      case 'next':
        this.stepChanged(this.activeStep + 1);
        return of(null);
    }
    const value = this.loadBalancerForm.value;
    this.createOrUpdate(
      this.loadBalancersApiService,
      value,
      true
    ).subscribe({
      next: () => {
        this.notificationService.showMessage($localize`Create load balancer scheduled`);
        const parsedUrl = this.router.parseUrl(this.config.getPrevUrl('openstack/load-balancers'));
        parsedUrl.queryParams = {};
        this.router.navigate([decodeURI(parsedUrl.toString())], {queryParamsHandling: 'merge'},
        ).catch(() => {
        });
      }, error: error => {
        if (error.error.load_balancer_details) {
          this.stepChanged(1);
          this.loadBalancerForm.controls.load_balancer_details.setErrors(error.error.load_balancer_details);
          this.loadBalancerForm.controls.load_balancer_details.markAllAsTouched();
        } else if (error.error.listener_details) {
          this.stepChanged(2);
          this.loadBalancerForm.controls.listener_details.setErrors(error.error.listener_details);
          this.loadBalancerForm.controls.listener_details.markAllAsTouched();
        } else if (error.error.listener_details) {
          this.stepChanged(3);
          this.loadBalancerForm.controls.pool_details.setErrors(error.error.pool_details);
          this.loadBalancerForm.controls.pool_details.markAllAsTouched();
        } else if (error.error.monitor_details) {
          this.stepChanged(5);
          this.loadBalancerForm.controls.monitor_details.setErrors(error.error.monitor_details);
          this.loadBalancerForm.controls.monitor_details.markAllAsTouched();
        }
      }
    });

    return of(null);
  }
}
