/*
Copyright (c) 2019-2023 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.

Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort

Please see README.md to learn where to place source code so that it
builds seamlessly.

Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/

package kvmvm

import (
	"encoding/json"
	"sort"
	"strconv"

	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	log "github.com/sirupsen/logrus"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/status"
)

func flattenDisks(disks []compute.InfoDisk) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	for _, disk := range disks {
		temp := map[string]interface{}{
			// "bus_number": disk.BusNumber,
			"disk_id": disk.ID,
			// "pci_slot":   disk.PCISlot,
			"sep_id": disk.SepID,
		}
		res = append(res, temp)
	}
	return res
}
func flattenQOS(qos compute.QOS) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"e_rate":   qos.ERate,
		"guid":     qos.GUID,
		"in_brust": qos.InBurst,
		"in_rate":  qos.InRate,
	}
	res = append(res, temp)
	return res
}
func flattenInterfaces(interfaces compute.ListInterfaces) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(interfaces))
	for _, interfaceItem := range interfaces {
		temp := map[string]interface{}{
			"bus_number":       interfaceItem.BusNumber,
			"conn_id":          interfaceItem.ConnID,
			"conn_type":        interfaceItem.ConnType,
			"def_gw":           interfaceItem.DefGW,
			"enabled":          interfaceItem.Enabled,
			"enable_secgroups": interfaceItem.EnableSecGroups,
			"flip_group_id":    interfaceItem.FLIPGroupID,
			"guid":             interfaceItem.GUID,
			"ip_address":       interfaceItem.IPAddress,
			"listen_ssh":       interfaceItem.ListenSSH,
			"mac":              interfaceItem.MAC,
			"mtu":              interfaceItem.MTU,
			"name":             interfaceItem.Name,
			"net_id":           interfaceItem.NetID,
			"netmask":          interfaceItem.NetMask,
			"net_type":         interfaceItem.NetType,
			"node_id":          interfaceItem.NodeID,
			"pci_slot":         interfaceItem.PCISlot,
			"qos":              flattenQOS(interfaceItem.QOS),
			"security_groups":  interfaceItem.SecGroups,
			"sdn_interface_id": interfaceItem.SDNInterfaceID,
			"target":           interfaceItem.Target,
			"type":             interfaceItem.Type,
			"trunk_tags":       interfaceItem.TrunkTags,
			"vnfs":             interfaceItem.VNFs,
			"libvirt_settings": flattenLibvirtSettings(interfaceItem.LibvirtSettings),
		}
		res = append(res, temp)
	}
	return res
}

func flattenLibvirtSettings(libvirtSettings compute.LibvirtSettings) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"guid":          libvirtSettings.GUID,
		"txmode":        libvirtSettings.TXMode,
		"ioeventfd":     libvirtSettings.IOEventFD,
		"event_idx":     libvirtSettings.EventIDx,
		"queues":        libvirtSettings.Queues,
		"rx_queue_size": libvirtSettings.RXQueueSize,
		"tx_queue_size": libvirtSettings.TXQueueSize,
	}
	res = append(res, temp)
	return res
}

func flattenSnapSets(snapSets compute.ListSnapSets) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(snapSets))
	for _, snapSet := range snapSets {
		temp := map[string]interface{}{
			"disks":     snapSet.Disks,
			"guid":      snapSet.GUID,
			"label":     snapSet.Label,
			"timestamp": snapSet.Timestamp,
		}
		res = append(res, temp)
	}
	return res
}
func flattenTags(tags map[string]string) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(tags))
	for key, val := range tags {
		temp := map[string]interface{}{
			"key": key,
			"val": val,
		}
		res = append(res, temp)
	}
	return res
}

func flattenListRules(listRules compute.ListRules) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(listRules))
	for _, rule := range listRules {
		temp := map[string]interface{}{
			"guid":     rule.GUID,
			"key":      rule.Key,
			"mode":     rule.Mode,
			"policy":   rule.Policy,
			"topology": rule.Topology,
			"value":    rule.Value,
		}
		res = append(res, temp)
	}
	return res
}
func flattenListACL(listAcl compute.ListACL) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(listAcl))
	for _, acl := range listAcl {
		temp := map[string]interface{}{
			"explicit":      acl.Explicit,
			"guid":          acl.GUID,
			"right":         acl.Right,
			"status":        acl.Status,
			"type":          acl.Type,
			"user_group_id": acl.UserGroupID,
		}
		res = append(res, temp)
	}
	return res
}

func flattenComputeList(computes *compute.ListComputes) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(computes.Data))
	for _, compute := range computes.Data {
		customFields, _ := json.Marshal(compute.CustomFields)
		devices, _ := json.Marshal(compute.Devices)
		temp := map[string]interface{}{
			"acl":                      flattenListACL(compute.ACL),
			"account_id":               compute.AccountID,
			"account_name":             compute.AccountName,
			"affinity_label":           compute.AffinityLabel,
			"affinity_rules":           flattenListRules(compute.AffinityRules),
			"affinity_weight":          compute.AffinityWeight,
			"anti_affinity_rules":      flattenListRules(compute.AntiAffinityRules),
			"arch":                     compute.Architecture,
			"auto_start_w_node":        compute.AutoStart,
			"boot_order":               compute.BootOrder,
			"bootdisk_size":            compute.BootDiskSize,
			"boot_image_id":            compute.BootImageID,
			"chipset":                  compute.Chipset,
			"cd_image_id":              compute.CdImageId,
			"clone_reference":          compute.CloneReference,
			"clones":                   compute.Clones,
			"computeci_id":             compute.ComputeCIID,
			"cpu_pin":                  compute.CPUPin,
			"cpus":                     compute.CPU,
			"created_by":               compute.CreatedBy,
			"created_time":             compute.CreatedTime,
			"custom_fields":            string(customFields),
			"deleted_by":               compute.DeletedBy,
			"deleted_time":             compute.DeletedTime,
			"desc":                     compute.Description,
			"devices":                  string(devices),
			"disks":                    flattenDisks(compute.Disks),
			"driver":                   compute.Driver,
			"gid":                      compute.GID,
			"guid":                     compute.GUID,
			"hp_backed":                compute.HPBacked,
			"compute_id":               compute.ID,
			"interfaces":               flattenInterfaces(compute.Interfaces),
			"live_migration_job_id":    compute.LiveMigrationJobID,
			"lock_status":              compute.LockStatus,
			"manager_id":               compute.ManagerID,
			"manager_type":             compute.ManagerType,
			"migrationjob":             compute.MigrationJob,
			"milestones":               compute.Milestones,
			"name":                     compute.Name,
			"need_reboot":              compute.NeedReboot,
			"numa_affinity":            compute.NumaAffinity,
			"numa_node_id":             compute.NumaNodeId,
			"os_version":               compute.OSVersion,
			"pinned":                   compute.PinnedToNode,
			"preferred_cpu":            compute.PreferredCPU,
			"qemu_guest":               flattenQemuQuest(compute.QemuQuest),
			"ram":                      compute.RAM,
			"reference_id":             compute.ReferenceID,
			"registered":               compute.Registered,
			"res_name":                 compute.ResName,
			"reserved_node_cpus":       compute.ReservedNodeCpus,
			"rg_id":                    compute.RGID,
			"rg_name":                  compute.RGName,
			"snap_sets":                flattenSnapSets(compute.SnapSets),
			"stateless_sep_id":         compute.StatelessSepID,
			"stateless_sep_type":       compute.StatelessSepType,
			"status":                   compute.Status,
			"tags":                     flattenTags(compute.Tags),
			"tech_status":              compute.TechStatus,
			"total_disk_size":          compute.TotalDiskSize,
			"updated_by":               compute.UpdatedBy,
			"updated_time":             compute.UpdatedTime,
			"user_managed":             compute.UserManaged,
			"read_only":                compute.ReadOnly,
			"vgpus":                    compute.VGPUs,
			"vins_connected":           compute.VINSConnected,
			"loader_type":              compute.LoaderType,
			"boot_type":                compute.BootType,
			"hot_resize":               compute.HotResize,
			"network_interface_naming": compute.NetworkInterfaceNaming,
			"zone_id":                  compute.ZoneID,
			"weight":                   compute.Weight,
		}
		res = append(res, temp)
	}

	return res
}

func flattenBootDisk(bootDisk *compute.ItemComputeDisk) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)

	temp := map[string]interface{}{
		"disk_name":         bootDisk.Name,
		"disk_id":           bootDisk.ID,
		"sep_id":            bootDisk.SepID,
		"shareable":         bootDisk.Shareable,
		"size_max":          bootDisk.SizeMax,
		"size_used":         bootDisk.SizeUsed,
		"pool":              bootDisk.Pool,
		"desc":              bootDisk.Description,
		"image_id":          bootDisk.ImageID,
		"size":              bootDisk.SizeMax,
		"present_to":        bootDisk.PresentTo,
		"storage_policy_id": bootDisk.StoragePolicyID,
		"to_clean":          bootDisk.ToClean,
		"cache":             bootDisk.Cache,
		"blk_discard":       bootDisk.BLKDiscard,
		"block_size":        bootDisk.BlockSize,
		"bus_number":        bootDisk.BusNumber,
		"provision":         bootDisk.Provision,
		"pci_slot":          bootDisk.PCISlot,
		"created_time":      bootDisk.CreatedTime,
		"created_by":        bootDisk.CreatedBy,
		"deleted_by":        bootDisk.DeletedBy,
		"deleted_time":      bootDisk.DeletedTime,
		"updated_time":      bootDisk.UpdatedTime,
		"devicename":        bootDisk.DeviceName,
	}

	res = append(res, temp)

	return res
}

func flattenComputeDisksDemo(disksList compute.ListComputeDisks, disksBlocks, extraDisks []interface{}, bootDiskId uint64) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(disksList))

	sort.Slice(disksList, func(i, j int) bool {
		return disksList[i].ID < disksList[j].ID
	})

	indexDataDisks := 0

	for _, disk := range disksList {
		if disk.ID == bootDiskId || findInExtraDisks(uint(disk.ID), extraDisks) { //skip main bootdisk and extraDisks
			continue
		}

		var pernamentlyValue bool
		if indexDataDisks < len(disksBlocks) {
			if diskBlock, ok := disksBlocks[indexDataDisks].(map[string]interface{}); ok {
				if perm, exists := diskBlock["permanently"]; exists {
					if permBool, ok := perm.(bool); ok {
						pernamentlyValue = permBool
					}
				}
			}
		}

		temp := map[string]interface{}{
			"devicename":        disk.DeviceName,
			"disk_name":         disk.Name,
			"disk_id":           disk.ID,
			"independent":       disk.Independent,
			"pci_slot":          disk.PCISlot,
			"bus_number":        disk.BusNumber,
			"sep_id":            disk.SepID,
			"shareable":         disk.Shareable,
			"size_max":          disk.SizeMax,
			"size_used":         disk.SizeUsed,
			"pool":              disk.Pool,
			"desc":              disk.Description,
			"image_id":          disk.ImageID,
			"size":              disk.SizeMax,
			"present_to":        disk.PresentTo,
			"storage_policy_id": disk.StoragePolicyID,
			"to_clean":          disk.ToClean,
			"created_by":        disk.CreatedBy,
			"created_time":      disk.CreatedTime,
			"deleted_by":        disk.DeletedBy,
			"deleted_time":      disk.DeletedTime,
			"updated_time":      disk.UpdatedTime,
			"permanently":       pernamentlyValue,
			"cache":             disk.Cache,
			"blk_discard":       disk.BLKDiscard,
			"block_size":        disk.BlockSize,
			"provision":         disk.Provision,
			"iotune":            flattenIotune(disk.IOTune),
		}
		res = append(res, temp)
		indexDataDisks++
	}

	return res
}

func flattenNetwork(networks []interface{}, interfaces compute.ListInterfaces) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(interfaces))

	for _, network := range interfaces {
		temp := map[string]interface{}{
			"net_id":           network.NetID,
			"net_type":         network.NetType,
			"ip_address":       network.IPAddress,
			"mac":              network.MAC,
			"mtu":              network.MTU,
			"net_mask":         network.NetMask,
			"sdn_interface_id": network.SDNInterfaceID,
			"weight":           flattenNetworkWeight(networks, network.NetID, network.NetType),
			"enabled":          network.Enabled,
		}
		res = append(res, temp)
	}
	return res
}

func flattenNetworkWeight(networks []interface{}, netID uint64, netType string) int {
	for _, network := range networks {
		ns := network.(map[string]interface{})
		if ns["net_id"].(int) == int(netID) && ns["net_type"].(string) == netType {
			weight := ns["weight"].(int)
			return weight
		}
	}
	return 0
}

func isBootDisk(diskConv map[string]interface{}, chipset string) bool {
	if chipset == "i440fx" {
		return diskConv["pci_slot"].(int) == 6
	}
	return diskConv["bus_number"].(int) == 6
}

func findBootDisk(disks compute.ListComputeDisks, chipset string) *compute.ItemComputeDisk {
	for _, disk := range disks {
		if chipset == "i440fx" {
			if disk.PCISlot == 6 {
				return &disk
			}
		} else {
			if disk.BusNumber == 6 {
				return &disk
			}
		}
	}
	return &compute.ItemComputeDisk{}
}

func flattenCompute(d *schema.ResourceData, computeRec compute.RecordCompute, pciList *compute.ListPCIDevices) error {
	// This function expects that compFacts string contains response from API compute/get,
	// i.e. detailed information about compute instance.
	//
	// NOTE: this function modifies ResourceData argument - as such it should never be called
	// from resourceComputeExists(...) method
	log.Debugf("flattenCompute: ID %d, RG ID %d", computeRec.ID, computeRec.RGID)

	devices, _ := json.Marshal(computeRec.Devices)
	bootDisk := findBootDisk(computeRec.Disks, computeRec.Chipset)

	//check extraDisks, ipa_type, is,
	d.SetId(strconv.FormatUint(computeRec.ID, 10))
	// d.Set("acl", flattenACL(computeRec.ACL))
	d.Set("account_id", computeRec.AccountID)
	d.Set("account_name", computeRec.AccountName)
	d.Set("affinity_weight", computeRec.AffinityWeight)
	d.Set("arch", computeRec.Architecture)
	d.Set("auto_start_w_node", computeRec.AutoStart)
	d.Set("boot_order", computeRec.BootOrder)
	// we intentionally use the SizeMax field, do not change it until the BootDiskSize field is fixed on the platform
	d.Set("boot_disk_size", bootDisk.SizeMax)
	d.Set("boot_disk", flattenBootDisk(bootDisk))
	d.Set("boot_disk_id", bootDisk.ID)
	d.Set("boot_image_id", computeRec.BootImageID)
	d.Set("cd_image_id", computeRec.CdImageId)
	d.Set("sep_id", bootDisk.SepID)
	d.Set("pool", bootDisk.Pool)
	d.Set("chipset", computeRec.Chipset)
	d.Set("clone_reference", computeRec.CloneReference)
	d.Set("clones", computeRec.Clones)
	d.Set("computeci_id", computeRec.ComputeCIID)
	d.Set("created_by", computeRec.CreatedBy)
	d.Set("created_time", computeRec.CreatedTime)
	// d.Set("custom_fields", flattenCustomFields(computeRec.CustomFields))
	d.Set("deleted_by", computeRec.DeletedBy)
	d.Set("deleted_time", computeRec.DeletedTime)
	d.Set("description", computeRec.Description)
	d.Set("devices", string(devices))
	err := d.Set("disks", flattenComputeDisksDemo(computeRec.Disks, d.Get("disks").([]interface{}), d.Get("extra_disks").(*schema.Set).List(), bootDisk.ID))
	if err != nil {
		return err
	}
	d.Set("driver", computeRec.Driver)
	d.Set("cpu", computeRec.CPU)
	d.Set("gid", computeRec.GID)
	d.Set("guid", computeRec.GUID)
	d.Set("compute_id", computeRec.ID)
	d.Set("image_id", computeRec.ImageID)
	d.Set("interfaces", flattenInterfaces(computeRec.Interfaces))
	d.Set("lock_status", computeRec.LockStatus)
	d.Set("manager_id", computeRec.ManagerID)
	d.Set("manager_type", computeRec.ManagerType)
	d.Set("migrationjob", computeRec.MigrationJob)
	d.Set("milestones", computeRec.Milestones)
	d.Set("name", computeRec.Name)
	d.Set("need_reboot", computeRec.NeedReboot)
	d.Set("numa_node_id", computeRec.NumaNodeId)
	d.Set("natable_vins_id", computeRec.NatableVINSID)
	d.Set("natable_vins_ip", computeRec.NatableVINSIP)
	d.Set("natable_vins_name", computeRec.NatableVINSName)
	d.Set("natable_vins_network", computeRec.NatableVINSNetwork)
	d.Set("natable_vins_network_name", computeRec.NatableVINSNetworkName)
	if err := d.Set("os_users", parseOsUsers(computeRec.OSUsers)); err != nil {
		return err
	}
	d.Set("pinned", computeRec.PinnedToNode)
	d.Set("preferred_cpu", computeRec.PreferredCPU)
	d.Set("ram", computeRec.RAM)
	d.Set("reference_id", computeRec.ReferenceID)
	d.Set("registered", computeRec.Registered)
	d.Set("res_name", computeRec.ResName)
	d.Set("reserved_node_cpus", computeRec.ReservedNodeCpus)
	d.Set("rg_id", computeRec.RGID)
	d.Set("rg_name", computeRec.RGName)
	d.Set("snap_sets", flattenSnapSets(computeRec.SnapSets))
	d.Set("stateless_sep_id", computeRec.StatelessSepID)
	d.Set("stateless_sep_type", computeRec.StatelessSepType)
	d.Set("status", computeRec.Status)
	// d.Set("tags", flattenTags(computeRec.Tags))
	d.Set("tech_status", computeRec.TechStatus)
	d.Set("updated_by", computeRec.UpdatedBy)
	d.Set("updated_time", computeRec.UpdatedTime)
	d.Set("user_managed", computeRec.UserManaged)
	d.Set("read_only", computeRec.ReadOnly)
	d.Set("vnc_password", computeRec.VNCPassword)
	d.Set("vgpus", flattenVGPUs(computeRec.VGPUs))
	d.Set("loader_type", computeRec.LoaderType)
	d.Set("boot_type", computeRec.BootType)
	d.Set("hot_resize", computeRec.HotResize)
	d.Set("network_interface_naming", computeRec.NetworkInterfaceNaming)
	d.Set("zone_id", computeRec.ZoneID)
	d.Set("loader_meta_iso", flattenLoaderMetaIso(computeRec.LoaderMetaIso))
	d.Set("cpu_pin", computeRec.CPUPin)
	d.Set("numa_affinity", computeRec.NumaAffinity)
	d.Set("hp_backed", computeRec.HPBacked)
	d.Set("weight", computeRec.Weight)

	d.Set("enabled", false)
	if computeRec.Status == status.Enabled {
		d.Set("enabled", true)
	}

	d.Set("started", false)
	if computeRec.TechStatus == "STARTED" {
		d.Set("started", true)
	}

	d.Set("network", flattenNetwork(d.Get("network").(*schema.Set).List(), computeRec.Interfaces))
	d.Set("pci_devices", flattenPCI(*pciList))

	d.Set("os_version", computeRec.OSVersion)

	return nil
}

func flattenLoaderMetaIso(loaderMetaIso compute.LoaderMetaIso) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"device_name": loaderMetaIso.DeviceName,
		"path":        loaderMetaIso.Path,
	}
	res = append(res, temp)
	return res
}

func flattenACL(acl compute.RecordACL) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"account_acl": flattenListACL(acl.AccountACL),
		"compute_acl": flattenListACL(acl.ComputeACL),
		"rg_acl":      flattenListACL(acl.RGACL),
	}
	res = append(res, temp)
	return res
}

func flattenAffinityRules(affinityRules compute.ListRules) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(affinityRules))
	for _, affinityRule := range affinityRules {
		temp := map[string]interface{}{
			"guid":     affinityRule.GUID,
			"key":      affinityRule.Key,
			"mode":     affinityRule.Mode,
			"policy":   affinityRule.Policy,
			"topology": affinityRule.Topology,
			"value":    affinityRule.Value,
		}
		res = append(res, temp)
	}

	return res
}

func flattenIotune(iotune compute.IOTune) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"read_bytes_sec":      iotune.ReadBytesSec,
		"read_bytes_sec_max":  iotune.ReadBytesSecMax,
		"read_iops_sec":       iotune.ReadIOPSSec,
		"read_iops_sec_max":   iotune.ReadIOPSSecMax,
		"size_iops_sec":       iotune.SizeIOPSSec,
		"total_bytes_sec":     iotune.TotalBytesSec,
		"total_bytes_sec_max": iotune.TotalBytesSecMax,
		"total_iops_sec":      iotune.TotalIOPSSec,
		"total_iops_sec_max":  iotune.TotalIOPSSecMax,
		"write_bytes_sec":     iotune.WriteBytesSec,
		"write_bytes_sec_max": iotune.WriteBytesSecMax,
		"write_iops_sec":      iotune.WriteIOPSSec,
		"write_iops_sec_max":  iotune.WriteIOPSSecMax,
	}
	res = append(res, temp)

	return res
}

func flattenSnapshots(snapshots compute.SnapshotExtendList) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(snapshots))
	for _, snapshot := range snapshots {
		temp := map[string]interface{}{
			"guid":          snapshot.GUID,
			"label":         snapshot.Label,
			"res_id":        snapshot.ResID,
			"reference_id":  snapshot.ReferenceID,
			"snap_set_guid": snapshot.SnapSetGUID,
			"snap_set_time": snapshot.SnapSetTime,
			"timestamp":     snapshot.TimeStamp,
		}
		res = append(res, temp)
	}

	return res
}

func flattenListComputeDisks(disks compute.ListComputeDisks) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(disks))
	for _, disk := range disks {
		acl, _ := json.Marshal(disk.ACL)
		temp := map[string]interface{}{
			"_ckey":                 disk.CKey,
			"acl":                   string(acl),
			"account_id":            disk.AccountID,
			"blk_discard":           disk.BLKDiscard,
			"block_size":            disk.BlockSize,
			"boot_partition":        disk.BootPartition,
			"bus_number":            disk.BusNumber,
			"created_time":          disk.CreatedTime,
			"cache":                 disk.Cache,
			"created_by":            disk.CreatedBy,
			"deleted_time":          disk.DeletedTime,
			"deleted_by":            disk.DeletedBy,
			"devicename":            disk.DeviceName,
			"description":           disk.Description,
			"destruction_time":      disk.DestructionTime,
			"disk_path":             disk.DiskPath,
			"gid":                   disk.GID,
			"guid":                  disk.GUID,
			"disk_id":               disk.ID,
			"image_id":              disk.ImageID,
			"images":                disk.Images,
			"iotune":                flattenIotune(disk.IOTune),
			"independent":           disk.Independent,
			"iqn":                   disk.IQN,
			"login":                 disk.Login,
			"milestones":            disk.Milestones,
			"name":                  disk.Name,
			"params":                disk.Params,
			"parent_id":             disk.ParentID,
			"passwd":                disk.Passwd,
			"pci_slot":              disk.PCISlot,
			"pool":                  disk.Pool,
			"present_to":            disk.PresentTo,
			"purge_time":            disk.PurgeTime,
			"replication":           flattenDiskReplication(disk.Replication),
			"reality_device_number": disk.RealityDeviceNumber,
			"res_id":                disk.ResID,
			"role":                  disk.Role,
			"sep_id":                disk.SepID,
			"shareable":             disk.Shareable,
			"size_available":        disk.SizeAvailable,
			"size_max":              disk.SizeMax,
			"size_used":             disk.SizeUsed,
			"snapshots":             flattenSnapshots(disk.Snapshots),
			"status":                disk.Status,
			"storage_policy_id":     disk.StoragePolicyID,
			"tech_status":           disk.TechStatus,
			"to_clean":              disk.ToClean,
			"provision":             disk.Provision,
			"updated_time":          disk.UpdatedTime,
		}
		res = append(res, temp)
	}

	return res
}

func flattenDiskReplication(rep compute.ItemReplication) []map[string]interface{} {
	res := []map[string]interface{}{
		{
			"disk_id":        rep.DiskID,
			"pool_id":        rep.PoolID,
			"role":           rep.Role,
			"self_volume_id": rep.SelfVolumeID,
			"storage_id":     rep.StorageID,
			"volume_id":      rep.VolumeID,
		},
	}
	return res
}

func flattenCustomFields(customFields map[string]interface{}) string {
	encoded, _ := json.Marshal(customFields)
	return string(encoded)
}

func flattenOsUsers(osUsers compute.ListOSUser) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(osUsers))
	for _, user := range osUsers {
		temp := map[string]interface{}{
			"guid":       user.GUID,
			"login":      user.Login,
			"password":   user.Password,
			"public_key": user.PubKey,
		}
		res = append(res, temp)
	}
	return res
}

func flattenDataCompute(d *schema.ResourceData, computeRec compute.RecordCompute, pciList *compute.ListPCIDevices) {
	devices, _ := json.Marshal(computeRec.Devices)
	userdata, _ := json.Marshal(computeRec.Userdata)
	d.Set("acl", flattenACL(computeRec.ACL))
	d.Set("account_id", computeRec.AccountID)
	d.Set("account_name", computeRec.AccountName)
	d.Set("affinity_label", computeRec.AffinityLabel)
	d.Set("affinity_rules", flattenAffinityRules(computeRec.AffinityRules))
	d.Set("affinity_weight", computeRec.AffinityWeight)
	d.Set("anti_affinity_rules", flattenListRules(computeRec.AntiAffinityRules))
	d.Set("auto_start_w_node", computeRec.AutoStart)
	d.Set("arch", computeRec.Architecture)
	d.Set("chipset", computeRec.Chipset)
	d.Set("boot_order", computeRec.BootOrder)
	d.Set("bootdisk_size", computeRec.BootDiskSize)
	d.Set("boot_image_id", computeRec.BootImageID)
	d.Set("cd_image_id", computeRec.CdImageId)
	d.Set("clone_reference", computeRec.CloneReference)
	d.Set("clones", computeRec.Clones)
	d.Set("computeci_id", computeRec.ComputeCIID)
	d.Set("cpu_pin", computeRec.CPUPin)
	d.Set("cpus", computeRec.CPU)
	d.Set("created_by", computeRec.CreatedBy)
	d.Set("created_time", computeRec.CreatedTime)
	d.Set("custom_fields", flattenCustomFields(computeRec.CustomFields))
	d.Set("deleted_by", computeRec.DeletedBy)
	d.Set("deleted_time", computeRec.DeletedTime)
	d.Set("desc", computeRec.Description)
	d.Set("devices", string(devices))
	d.Set("disks", flattenListComputeDisks(computeRec.Disks))
	d.Set("driver", computeRec.Driver)
	d.Set("gid", computeRec.GID)
	d.Set("guid", computeRec.GUID)
	d.Set("hp_backed", computeRec.HPBacked)
	d.Set("compute_id", computeRec.ID)
	d.Set("image_id", computeRec.ImageID)
	d.Set("image_name", computeRec.ImageName)
	d.Set("interfaces", flattenInterfaces(computeRec.Interfaces))
	d.Set("live_migration_job_id", computeRec.LiveMigrationJobID)
	d.Set("lock_status", computeRec.LockStatus)
	d.Set("manager_id", computeRec.ManagerID)
	d.Set("manager_type", computeRec.ManagerType)
	d.Set("migrationjob", computeRec.MigrationJob)
	d.Set("milestones", computeRec.Milestones)
	d.Set("name", computeRec.Name)
	d.Set("need_reboot", computeRec.NeedReboot)
	d.Set("numa_affinity", computeRec.NumaAffinity)
	d.Set("numa_node_id", computeRec.NumaNodeId)
	d.Set("natable_vins_id", computeRec.NatableVINSID)
	d.Set("natable_vins_ip", computeRec.NatableVINSIP)
	d.Set("natable_vins_name", computeRec.NatableVINSName)
	d.Set("natable_vins_network", computeRec.NatableVINSNetwork)
	d.Set("natable_vins_network_name", computeRec.NatableVINSNetworkName)
	d.Set("os_users", flattenOsUsers(computeRec.OSUsers))
	d.Set("pinned", computeRec.PinnedToNode)
	d.Set("preferred_CPU", computeRec.PreferredCPU)
	d.Set("qemu_guest", flattenQemuQuest(computeRec.QemuQuest))
	d.Set("ram", computeRec.RAM)
	d.Set("reference_id", computeRec.ReferenceID)
	d.Set("registered", computeRec.Registered)
	d.Set("res_name", computeRec.ResName)
	d.Set("reserved_node_cpus", computeRec.ReservedNodeCpus)
	d.Set("rg_id", computeRec.RGID)
	d.Set("rg_name", computeRec.RGName)
	d.Set("snap_sets", flattenSnapSets(computeRec.SnapSets))
	d.Set("stateless_sep_id", computeRec.StatelessSepID)
	d.Set("stateless_sep_type", computeRec.StatelessSepType)
	d.Set("status", computeRec.Status)
	d.Set("tags", computeRec.Tags)
	d.Set("tech_status", computeRec.TechStatus)
	d.Set("updated_by", computeRec.UpdatedBy)
	d.Set("updated_time", computeRec.UpdatedTime)
	d.Set("user_managed", computeRec.UserManaged)
	d.Set("read_only", computeRec.ReadOnly)
	d.Set("userdata", string(userdata))
	d.Set("vnc_password", computeRec.VNCPassword)
	d.Set("vgpus", flattenVGPUs(computeRec.VGPUs))
	d.Set("pci_devices", flattenPCI(*pciList))
	d.Set("loader_type", computeRec.LoaderType)
	d.Set("boot_type", computeRec.BootType)
	d.Set("hot_resize", computeRec.HotResize)
	d.Set("network_interface_naming", computeRec.NetworkInterfaceNaming)
	d.Set("zone_id", computeRec.ZoneID)
	d.Set("loader_meta_iso", flattenLoaderMetaIso(computeRec.LoaderMetaIso))
	d.Set("os_version", computeRec.OSVersion)
	d.Set("weight", computeRec.Weight)
}

func flattenPCI(pciList compute.ListPCIDevices) []uint64 {
	res := make([]uint64, 0, len(pciList.Data))

	for _, v := range pciList.Data {
		res = append(res, v.ID)
	}

	return res
}

func flattenComputeAudits(computeAudits compute.ListAudits) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(computeAudits.Data))
	for _, computeAudit := range computeAudits.Data {
		temp := map[string]interface{}{
			"call":         computeAudit.Call,
			"responsetime": computeAudit.ResponseTime,
			"statuscode":   computeAudit.StatusCode,
			"timestamp":    computeAudit.Timestamp,
			"user":         computeAudit.User,
		}
		res = append(res, temp)
	}
	return res
}

func flattenPfwList(computePfws *compute.ListPFWs) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(computePfws.Data))
	for _, computePfw := range computePfws.Data {
		temp := map[string]interface{}{
			"pfw_id":            computePfw.ID,
			"local_ip":          computePfw.LocalIP,
			"local_port":        computePfw.LocalPort,
			"protocol":          computePfw.Protocol,
			"public_port_end":   computePfw.PublicPortEnd,
			"public_port_start": computePfw.PublicPortStart,
			"vm_id":             computePfw.VMID,
		}
		res = append(res, temp)
	}
	return res
}

func flattenUserList(d *schema.ResourceData, userList *compute.ListUsers) {
	d.Set("account_acl", flattenListACL(userList.Data.AccountACL))
	d.Set("compute_acl", flattenListACL(userList.Data.ComputeACL))
	d.Set("rg_acl", flattenListACL(userList.Data.RGACL))
}

func flattenComputeGetAudits(computeAudits compute.ListShortAudits) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(computeAudits))
	for _, computeAudit := range computeAudits {
		temp := map[string]interface{}{
			"epoch":   computeAudit.Epoch,
			"message": computeAudit.Message,
		}
		res = append(res, temp)
	}
	return res
}

func flattenSnapshotUsage(computeSnapshotUsages compute.ListUsageSnapshots) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(computeSnapshotUsages))
	for _, computeUsage := range computeSnapshotUsages {
		temp := map[string]interface{}{
			"count":     computeUsage.Count,
			"stored":    computeUsage.Stored,
			"label":     computeUsage.Label,
			"timestamp": computeUsage.Timestamp,
		}
		res = append(res, temp)
	}
	return res
}

// func flattenSnapshotList(computeSnapshotUsages *compute.ListSnapShots) []map[string]interface{} {
// 	res := make([]map[string]interface{}, 0, len(computeSnapshotUsages.Data))
// 	for _, computeUsage := range computeSnapshotUsages.Data {
// 		temp := map[string]interface{}{
// 			"disks":     computeUsage.Disks,
// 			"guid":      computeUsage.GUID,
// 			"label":     computeUsage.Label,
// 			"timestamp": computeUsage.Timestamp,
// 		}
// 		res = append(res, temp)
// 	}
// 	return res
// }

func flattenVGPU(vgpuList []compute.ItemVGPU) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(vgpuList))
	for _, dev := range vgpuList {
		temp := map[string]interface{}{
			"account_id":       dev.AccountID,
			"created_time":     dev.CreatedTime,
			"deleted_time":     dev.DeletedTime,
			"gid":              dev.GID,
			"guid":             dev.GUID,
			"vgpu_id":          dev.ID,
			"last_claimed_by":  dev.LastClaimedBy,
			"last_update_time": dev.LastUpdateTime,
			"mode":             dev.Mode,
			"pci_slot":         dev.PCISlot,
			"pgpuid":           dev.PGPUID,
			"profile_id":       dev.ProfileID,
			"ram":              dev.RAM,
			"reference_id":     dev.ReferenceID,
			"rg_id":            dev.RGID,
			"status":           dev.Status,
			"type":             dev.Type,
			"vm_id":            dev.VMID,
		}
		res = append(res, temp)
	}
	return res
}

func flattenPCIDevice(deviceList []compute.ItemPCIDevice) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(deviceList))
	for _, dev := range deviceList {
		temp := map[string]interface{}{
			"compute_id":  dev.ComputeID,
			"description": dev.Description,
			"guid":        dev.GUID,
			"hwpath":      dev.HwPath,
			"device_id":   dev.ID,
			"name":        dev.Name,
			"rg_id":       dev.RGID,
			"node_id":     dev.NodeID,
			"status":      dev.Status,
			"system_name": dev.SystemName,
		}
		res = append(res, temp)
	}
	return res
}

func flattenVGPUs(vgpus []compute.VGPUItem) []map[string]interface{} {
	res := make([]map[string]interface{}, len(vgpus))

	for i, vgpu := range vgpus {

		res[i] = map[string]interface{}{
			"id":               int(vgpu.ID),
			"gid":              int(vgpu.GID),
			"type":             vgpu.Type,
			"mode":             vgpu.Mode,
			"status":           vgpu.Status,
			"profile_id":       vgpu.ProfileID,
			"ram":              int(vgpu.RAM),
			"last_update_time": int(vgpu.LastUpdateTime),
			"created_time":     int(vgpu.CreatedTime),
			"deleted_time":     int(vgpu.DeletedTime),
			"vmid":             int(vgpu.VMID),
			"pgpuid":           int(vgpu.PGPuid),
			"reference_id":     vgpu.ReferenceID,
			"account_id":       int(vgpu.AccountID),
			"rg_id":            int(vgpu.RgID),
			"last_claimed_by":  int(vgpu.LastClaimedBy),
			"pci_slot":         int(vgpu.PCISlot),
			"bus_number":       int(vgpu.BusNumber),
			"guid":             int(vgpu.GUID),
		}
	}

	return res
}

func flattenQemuQuest(qemuQuest compute.QemuQuest) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)

	temp := map[string]interface{}{
		"enabled":                qemuQuest.Enabled,
		"enabled_agent_features": qemuQuest.EnabledAgentFeatures,
		"guid":                   qemuQuest.GUID,
		"last_update":            uint64(qemuQuest.LastUpdate),
		"user":                   qemuQuest.User,
	}

	res = append(res, temp)

	return res
}
