import { Injectable } from '@angular/core';
import { ConfigService } from '@shared/config/config.service';
import { IInstanceModel } from '../model/instance.model';
import { FleioApiService } from '../../fleio-api.service';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { IInstanceSummary } from '@fleio-api/openstack/model/instance-summary.model';
import { FleioId } from '@fleio-api/base-model/base-fleio-object.model';
import { ISecurityGroupModel } from '@fleio-api/openstack/model/security-group.model';
import { IOpenstackBackupSchedule } from '@fleio-api/openstack/model/openstack-backup-schedule.model';
import { IImageModel } from '@fleio-api/openstack/model/image.model';
import { IFlavorModel } from '@fleio-api/openstack/model/flavor.model';
import { ITrafficData } from '@fleio-api/openstack/model/traffic-data.model';
import { FleioObjectsList } from '@fleio-api/fleio-objects-list';
import { IVolumeModel } from '@fleio-api/openstack/model/volume.model';

@Injectable({
  providedIn: 'root'
})
export class InstancesApiService extends FleioApiService<IInstanceModel> {
  constructor(protected httpClient: HttpClient, protected config: ConfigService) {
    super();
    this.setEndpoint(config.getPanelApiUrl('openstack/instances'));
  }

  getSummary(): Observable<IInstanceSummary> {
    return this.getAction('summary');
  }

  detachPort(instanceId: FleioId, portId: FleioId) {
    return this.objectPostAction(instanceId, 'detach_port', {
      port_id: portId,
    });
  }

  removeFloatingIp(instanceId: FleioId, ip: string) {
    return this.objectPostAction(instanceId, 'remove_floating_ip', {
      ip,
    });
  }

  attachPort(instanceId: FleioId, portId: FleioId) {
    return this.objectPostAction(instanceId, 'attach_port', {
      port_id: portId,
    });
  }

  getFloatingIPNetworks(selectedNetworks, region: string) {
    return this.postAction('get_floating_ip_networks', {
      nics: selectedNetworks,
      region: region,
    });
  }

  associateIp(instanceId: FleioId, floatingIp: string, fixedIp: string) {
    return this.objectPostAction(instanceId, 'add_floating_ip', {
      floating_ip: floatingIp,
      fixed_ip: fixedIp,
    });
  }

  dissociateIp(instanceId: FleioId, ip: string) {
    return this.objectPostAction(instanceId, 'remove_floating_ip', {
      ip,
    });
  }

  getAssociateIpCreateOptions(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'associate_ip_create_options');
  }

  getDissociateIpOptions(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'dissociate_ip_create_options');
  }

  getSecurityGroups(instanceId: FleioId): Observable<{ security_groups: Array<ISecurityGroupModel> }> {
    return this.objectGetAction(instanceId, 'list_security_groups');
  }

  getTrafficData(instanceId: FleioId): Observable<ITrafficData> {
    return this.objectGetAction(instanceId, 'traffic-data');
  }

  getAttachableVolumes(instanceId: FleioId, pageSize): Observable<FleioObjectsList<IVolumeModel>> {
    return this.objectGetAction(instanceId, 'get_attachable_volumes', {
      page_size: pageSize
    });
  }

  associateSecurityGroup(instanceId: FleioId, securityGroupId: FleioId) {
    return this.objectPostAction(instanceId, 'associate_security_group', {
      group: securityGroupId,
    });
  }

  dissociateSecurityGroup(instanceId: FleioId, securityGroupId: FleioId) {
    return this.objectPostAction(instanceId, 'dissociate_security_group', {
      group: securityGroupId,
    });
  }

  getAssociateSecurityGroupOptions(instanceId: FleioId): Observable<{ groups: Array<ISecurityGroupModel> }> {
    return this.objectGetAction(instanceId, 'associate_security_group_create_options');
  }

  getInstanceBackupsAndBackupSchedules(instanceId: FleioId): Observable<{
    schedules: Array<IOpenstackBackupSchedule>;
    backups: Array<IImageModel>;
  }> {
    return this.objectGetAction(instanceId, 'get_instance_backups_and_backup_schedules');
  }

  createBackup(instanceId: FleioId, backupData: Record<string, any>) {
    return this.objectPostAction(instanceId, 'create_backup', backupData);
  }

  getFlavorRelatedImages(flavor: IFlavorModel, region, clientId): Observable<{
    bootSources: {
      image: Array<IImageModel>;
      owned_image: Array<IImageModel>;
      shared_image: Array<IImageModel>;
      community_image: Array<IImageModel>;
    };
  }> {
    const params = {
      region
    };
    if (flavor) {
      // @ts-ignore
      params.selected_flavor_id = flavor.id;
    }
    if (clientId) {
      // @ts-ignore
      params.selected_client_id = clientId;
    }
    return this.getAction('get_images_assigned_to_flavors_or_flavor_group', params);
  }

  getFlavorsAssignedToImage(imageId, clientId, onlyCompatibleFlavors = false) {
    const params = {
      image_id: imageId,
      only_compatible_flavors: onlyCompatibleFlavors,
    };
    if (clientId) {
      // @ts-ignore
      params.selected_client_id = clientId;
    }
    return this.getAction('get_flavors_assigned_to_image', params);
  }

  changePassword(instanceId: FleioId, newPassword: string): Observable<any> {
    return this.objectPostAction(instanceId, 'change_password', {password: newPassword});
  }

  attachVolume(instanceId: FleioId, parameters: Record<string, any>): Observable<any> {
    return this.objectPostAction(
      instanceId, 'attach_volume', parameters
    );
  }

  createSnapshot(instanceId: FleioId, parameters: Record<string, any>): Observable<any> {
    return this.objectPostAction(instanceId, 'create_snapshot', parameters);
  }

  migrate(instanceId: FleioId, parameters: Record<string, any>): Observable<any> {
    return this.objectPostAction(instanceId, 'migrate', parameters);
  }

  move(instanceId: FleioId, clientId: FleioId): Observable<any> {
    return this.objectPostAction(instanceId, 'move', {client: clientId,});
  }

  rename(instanceId: FleioId, newName: string): Observable<any> {
    return this.objectPostAction(instanceId, 'rename', {name: newName});
  }

  detachVolume(instanceId: FleioId, volumeId: FleioId): Observable<any> {
    return this.objectPostAction(instanceId, 'detach_volume', {volume_id: volumeId});
  }

  rebuild(instanceId: FleioId, parameters: Record<string, any>): Observable<any> {
    return this.objectPostAction(instanceId, 'rebuild', parameters);
  }

  reboot(instanceId: FleioId, parameters: Record<string, any>): Observable<any> {
    return this.objectPostAction(instanceId, 'reboot', parameters);
  }

  resize(instanceId: FleioId, flavorId: FleioId): Observable<any> {
    return this.objectPostAction(instanceId, 'resize', {flavor: flavorId});
  }

  rescue(instanceId: FleioId, parameters: Record<string, any>): Observable<any> {
    return this.objectPostAction(instanceId, 'rescue', parameters);
  }

  bootFromISO(instanceId: FleioId, parameters: Record<string, any>): Observable<any> {
    return this.objectPostAction(instanceId, 'boot_from_iso', parameters);
  }

  getConsoleUrl(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'get_console_url');
  }

  getAddIpOptions(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'add_ip_options');
  }

  getAddPortOptions(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'add_port_options');
  }

  getMigrateOptions(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'migrate_options');
  }

  getActions(instanceId: FleioId, pageSize: number, marker: string = null): Observable<any> {
    const queryParams = {
      limit: pageSize,
    } as Record<string, any>;
    if (marker) {
      queryParams.marker = marker;
    }
    return this.objectGetAction(instanceId, 'actions', queryParams);
  }

  getActionDetails(instanceId: FleioId, requestId: FleioId): Observable<any> {
    return this.objectGetAction(
      instanceId,
      'action_details',
      {
        request_id: requestId
      }
    );
  }

  getMeasures(instanceId: FleioId, hours, metric): Observable<any> {
    return this.objectGetAction(instanceId, 'measures', {hours, metric});
  }

  getConsoleOutput(instanceId: FleioId, length): Observable<any> {
    return this.objectGetAction(instanceId, 'get_console_output', {length});
  }

  getVolumeAttachments(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'get_volume_attachments', {});
  }

  getPrice(data): Observable<any> {
    return this.getAction('get_price', data);
  }

  getSnapshotPricePerCycle(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'get_snapshot_price_per_cycle', {});
  }

  getBackupPrice(instanceId: FleioId): Observable<any> {
    return this.objectGetAction(instanceId, 'get_backup_price', {});
  }

  unshelve(instanceId: FleioId, availabilityZone: string): Observable<any> {
    return this.objectPostAction(instanceId, 'unshelve', {availability_zone: availabilityZone});
  }
}
