import { Component, OnInit, ViewChild } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { IImageModel } from '@fleio-api/openstack/model/image.model';
import { EMPTY, Observable, of } from 'rxjs';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { ImagesApiService } from '@fleio-api/openstack/image/image-api.service';
import { ActivatedRoute, Router } from '@angular/router';
import { ConfigService } from '@shared/config/config.service';
import { IImageCreateOptionsModel } from '@fleio-api/openstack/model/image-create-options.model';
import { HttpEvent, HttpEventType } from '@angular/common/http';
import { NotificationService } from '@shared/ui-api/notification.service';
import { debounceTime } from 'rxjs/operators';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { MatChipEditedEvent, MatChipInputEvent } from '@angular/material/chips';
import { ValidatorsHelper } from '@shared/ui-api/helpers/validators-helper';

@Component({
  selector: 'app-image-edit-form',
  templateUrl: './image-edit-form.component.html',
  styleUrls: ['./image-edit-form.component.scss']
})
export class ImageEditFormComponent extends DetailsFormBase<IImageModel> implements OnInit {
  imageForm = this.formBuilder.group({
    region: ['', Validators.required],
    name: ['', Validators.required],
    min_disk: [0, [ValidatorsHelper.requiredIntegerValidator, Validators.min(0)]],
    min_ram: [0, [ValidatorsHelper.requiredIntegerValidator, Validators.min(0)]],
    disk_format: ['', Validators.required],
    os_distro: [''],
    os_version: [''],
    architecture: [''],
    hypervisor_type: [''],
    tags: [[]],
    visibility: ['private', Validators.required],
    protected: [false],
    source: ['url', Validators.required],
    url: ['', Validators.required],
    file: ['', Validators.required],
  });
  createOptions: IImageCreateOptionsModel;
  @ViewChild('fileInput') fileInput: any;

  readonly separatorKeysCodes = [ENTER, COMMA] as const;
  imageFile: File;
  uploading = false;
  uploadProgress = 7;
  fromInstanceCreate = (this.activatedRoute.snapshot.queryParams?.from_instance_create === 'true') || false;
  estimatedCost: string;
  estimatedCostLoading = false;
  selectClient = this.config.authService.feature('ui.openstack.image.select-client');
  newImage = false;
  createInClientProject = false;

  constructor(
    private formBuilder: UntypedFormBuilder,
    private imagesApi: ImagesApiService,
    private router: Router,
    public config: ConfigService,
    private activatedRoute: ActivatedRoute,
    private notificationService: NotificationService,
  ) {
    super();
  }

  changeDestination(value: boolean) {
    // createInClientProject
    if (value === false) {
      this.imageForm.removeControl('client');
    } else {
      this.imageForm.addControl('client', this.formBuilder.control('', Validators.required));
    }
    this.estimatedCost = null;
  }


  protected tabActivated() {
    super.tabActivated();
    if (this.objectController) {
      this.objectController.actionCallback = () => this.saveImage();
    }
  }

  ngOnInit() {
    super.ngOnInit();
    this.createOptions = this.activatedRoute.snapshot.data.createOptions;

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

    if (this.object && !this.object.id) {
      if (this.createOptions.disk_formats.indexOf('qcow2') > -1) {
        this.imageForm.controls.disk_format.setValue('qcow2');
      } else {
        this.imageForm.controls.disk_format.setValue(this.createOptions.disk_formats[0]);
      }
      this.imageForm.controls.architecture.setValue(this.createOptions.architectures[0][0]);
    }
    if (this.objectController) {
      this.objectController.actionCallback = () => this.saveImage();
    } else {
      console.error(`Cannot set actionCallback due to missing objectController`);
    }
    if (this.object) {
      this.imageForm.patchValue(this.object);
    } else {
    }

    if (this.object && !this.object.id) {
      // creating new image
      this.newImage = true;
      this.imageForm.controls.region.setValue(this.createOptions.selected_region);
      this.imageForm.controls.file.disable();
      this.imageForm.controls.source.valueChanges.subscribe((newValue) => {
        if (newValue === 'url') {
          this.imageForm.controls.file.disable();
          this.imageForm.controls.url.enable();
        } else {
          this.imageForm.controls.file.enable();
          this.imageForm.controls.url.disable();
        }
      });
    } else {
      this.imageForm.controls.source.disable();
      this.imageForm.controls.url.disable();
      this.imageForm.controls.file.disable();
    }
  }

  fileChanged($event: Event) {
    const files = ($event.target as HTMLInputElement).files;
    this.imageFile = files.length > 0 ? files[0] : null;
    this.imageForm.controls.file.setValue(this.imageFile.name);
  }

  tagRemoved(tagToRemove: string) {
    let tags = this.imageForm.controls.tags.value as Array<string>;
    tags = tags.filter(tag => tag !== tagToRemove);
    this.imageForm.controls.tags.setValue(tags);
  }

  tagEdited(tag: string, event: MatChipEditedEvent) {
    const value = event.value.trim();

    // Remove tag if it no longer has a value
    if (!value) {
      this.tagRemoved(tag);
      return;
    }

    // Edit existing tag
    let tags = this.imageForm.controls.tags.value as Array<string>;
    const index = tags.indexOf(tag);
    if (index >= 0) {
      tags[index] = value;
      this.imageForm.controls.tags.setValue(tags);
    }
  }

  addTag(event: MatChipInputEvent) {
    const value = (event.value || '').trim();
    if (value) {
      const tags = this.imageForm.controls.tags.value as Array<string>;
      if (tags) {
        tags.push(value);
        this.imageForm.controls.tags.setValue(tags);
      } else {
        this.imageForm.controls.tags.setValue([value]);
      }
    }

    // Clear the input value
    event.chipInput!.clear();
  }

  saveImage(): Observable<IActionResult> {
    const imageData = this.imageForm.value;
    let formData: FormData;

    if (!this.object?.id) {
      if (imageData.client && imageData.client.id) {
        imageData.client = imageData.client.id;
      }
      if (!this.createInClientProject) {
        imageData.create_as_admin = true;
      }

      formData = new FormData();
      if (imageData.file) {
        delete imageData.file;
      }
      Object.keys(imageData).map(fieldName => {
        let formDataFieldValue: any;
        if (fieldName === 'tags') {
          formDataFieldValue = JSON.stringify(imageData[fieldName]);
        } else {
          formDataFieldValue = imageData[fieldName];
        }
        formData.append(fieldName, formDataFieldValue);
      });
      if (imageData.source === 'file' && this.imageFile) {
        formData.append('file', this.imageFile, this.imageFile.name);
      }
    }

    return new Observable<IActionResult>((subscriber) => {
      const response = this.createOrUpdate(
        this.imagesApi,
        formData ? formData : imageData,
      );
      if (this.imageForm.invalid) {
        return subscriber.complete();
      }

      if (response !== EMPTY) {
        this.uploading = !!formData;
        response.subscribe({
          next: (data) => {
            let requestCompleted = false;
            if (formData) {
              const event = data as HttpEvent<IImageModel>;
              if (event.type === HttpEventType.UploadProgress) {
                this.uploadProgress = event.loaded / event.total * 100;
              }

              if (event.type === HttpEventType.Response) {
                requestCompleted = true;
              }
            } else {
              requestCompleted = true;
            }

            if (requestCompleted) {
              this.uploading = false;
              if (this.object.id) {
                this.notificationService.showMessage($localize`Image updated`);
              } else {
                this.notificationService.showMessage($localize`Image creation scheduled`);
                if (this.fromInstanceCreate) {
                  // if user came from instance create, redirect back having the image preselected
                  const image = (data as any).body as IImageModel;
                  this.router.navigateByUrl(
                    this.config.getPanelUrl(
                      `openstack/instances/create?image_id=${image.id}&region=${image.region}`,
                    )
                  ).catch(() => {
                  });
                  return of(null);
                }
              }
              this.redirectAfterCreate('openstack/images', this.router, this.config);
            }
          },
        }).add(() => {
          this.uploading = false;
          subscriber.complete();
        });
      }
    });
  }

  getSimulatedPrice() {
    if (this.authSvc.feature('openstack.images.show-price-on-create') && !this.object.id) {
      const region = this.imageForm.controls.region.value;
      const diskFormat = this.imageForm.controls.disk_format.value;
      const visibility = this.imageForm.controls.visibility.value;
      if (region && diskFormat && visibility) {
        if (this.imageForm.controls.source.value === 'file' && this.imageFile) {
          const params = {
            region,
            visibility,
            disk_format: diskFormat,
            size: this.imageFile.size.toString(),
          };
          this.estimatedCostLoading = true;
          this.imagesApi.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 {
          // do not estimate text for URL source
          this.estimatedCost = null;
        }
      }
    }
  }
}
