package kvmvm

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

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

func flattenCompute(d *schema.ResourceData, computeRec *compute.RecordCompute, pciList *compute.ListPCIDevices) error {
	log.Debugf("flattenCompute: ID %d, RG ID %d", computeRec.ID, computeRec.RGID)

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

	if len(computeRec.Interfaces) > 0 {
		log.Debugf("flattenCompute: calling parseComputeInterfacesToNetworks for %d interfaces", len(computeRec.Interfaces))
		if err := d.Set("network", parseComputeInterfacesToNetworks(d.Get("network").(*schema.Set).List(), computeRec.Interfaces)); err != nil {
			return err
		}
	}
	d.Set("account_id", computeRec.AccountID)
	d.Set("account_name", computeRec.AccountName)
	d.Set("acl", flattenListACLInterface(computeRec.ACL))
	d.Set("affinity_label", computeRec.AffinityLabel)
	d.Set("affinity_weight", computeRec.AffinityWeight)
	d.Set("affinity_rules", flattenAffinityRules(computeRec.AffinityRules))
	d.Set("anti_affinity_rules", flattenAffinityRules(computeRec.AntiAffinityRules))
	d.Set("arch", computeRec.Arch)
	d.Set("auto_start_w_node", computeRec.AutoStart)
	d.Set("boot_order", computeRec.BootOrder)
	d.Set("boot_disk_id", bootDisk.ID)
	// 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_image_id", bootDisk.ImageID)
	d.Set("chipset", computeRec.Chipset)
	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", computeRec.CPUs)
	d.Set("created_by", computeRec.CreatedBy)
	d.Set("created_time", computeRec.CreatedTime)
	d.Set("custom_fields", string(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", flattenComputeDisks(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("gid", computeRec.GID)
	d.Set("guid", computeRec.GUID)
	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("lock_status", computeRec.LockStatus)
	d.Set("loader_meta_iso", flattenLoaderMetaIso(computeRec.LoaderMetaIso))
	d.Set("manager_id", computeRec.ManagerID)
	d.Set("manager_type", computeRec.ManagerType)
	d.Set("migrationjob", computeRec.MigrationJob)
	d.Set("milestones", computeRec.Milestones)
	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("need_reboot", computeRec.NeedReboot)
	d.Set("numa_node_id", computeRec.NumaNodeId)
	d.Set("os_users", flattenOSUsers(computeRec.OSUsers))
	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_name", computeRec.RGName)
	d.Set("rg_id", computeRec.RGID)
	d.Set("snap_sets", flattenSnapSets(computeRec.SnapSets))
	d.Set("started", computeRec.TechStatus == "STARTED")
	d.Set("node_id", computeRec.NodeID)
	d.Set("node_name", computeRec.NodeName)
	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_data", string(userData))
	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("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("os_version", computeRec.OSVersion)
	d.Set("cpu_pin", computeRec.CPUPin)
	d.Set("numa_affinity", computeRec.NumaAffinity)
	d.Set("hp_backed", computeRec.HPBacked)
	d.Set("weight", computeRec.Weight)
	return nil
}

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 flattenTags(tags map[string]interface{}) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(tags))

	for k, v := range tags {
		res = append(res, map[string]interface{}{
			"key": k,
			"val": v,
		})
	}

	return res
}

func flattenSnapSets(snaps compute.ListSnapshots) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(snaps))

	for _, snap := range snaps {
		res = append(res, map[string]interface{}{
			"disks":     snap.Disks,
			"guid":      snap.GUID,
			"label":     snap.Label,
			"timestamp": snap.Timestamp,
		})
	}

	return res
}

func flattenOSUsers(users compute.ListOSUsers) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(users))

	for _, user := range users {
		res = append(res, map[string]interface{}{
			"guid":       user.GUID,
			"login":      user.Login,
			"password":   user.Password,
			"public_key": user.PubKey,
		})
	}

	return res
}

func flattenInterfaces(ifaces compute.ListInterfaces) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(ifaces))
	for _, iface := range ifaces {
		res = append(res, map[string]interface{}{
			"bus_number":       iface.BusNumber,
			"conn_id":          iface.ConnID,
			"conn_type":        iface.ConnType,
			"def_gw":           iface.DefGW,
			"enabled":          iface.Enabled,
			"enable_secgroups": iface.EnableSecGroups,
			"flip_group_id":    iface.FLIPGroupID,
			"guid":             iface.GUID,
			"ip_address":       iface.IPAddress,
			"listen_ssh":       iface.ListenSSH,
			"mac":              iface.MAC,
			"mtu":              iface.MTU,
			"name":             iface.Name,
			"net_id":           iface.NetID,
			"netmask":          iface.NetMask,
			"net_type":         iface.NetType,
			"node_id":          iface.NodeID,
			"pci_slot":         iface.PCISlot,
			"qos":              flattenQOS(iface.QOS),
			"sdn_interface_id": iface.SDNInterfaceID,
			"security_groups":  iface.SecGroups,
			"target":           iface.Target,
			"type":             iface.Type,
			"trunk_tags":       iface.TrunkTags,
			"vnfs":             iface.VNFs,
			"libvirt_settings": flattenLibvirtSettings(iface.LibvirtSettings),
		})
	}
	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 flattenQOS(qos compute.QOS) []map[string]interface{} {
	return []map[string]interface{}{
		{
			"e_rate":   qos.ERate,
			"guid":     qos.GUID,
			"in_brust": qos.InBurst,
			"in_rate":  qos.InRate,
		},
	}
}

func flattenComputeDisks(disksList compute.ListDisks, 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
					}
				}
			}
		}

		var nodeIds *schema.Set
		if indexDataDisks < len(disksBlocks) {
			if diskBlock, ok := disksBlocks[indexDataDisks].(map[string]interface{}); ok {
				if nodeIds, exists := diskBlock["node_ids"]; exists {
					nodeIds = nodeIds.(*schema.Set)
				}
			}
		}

		temp := map[string]interface{}{
			"disk_name":         disk.Name,
			"node_ids":          nodeIds,
			"size":              disk.SizeMax,
			"sep_id":            disk.SEPID,
			"pci_slot":          disk.PCISlot,
			"bus_number":        disk.BusNumber,
			"pool":              disk.Pool,
			"desc":              disk.Description,
			"image_id":          disk.ImageID,
			"independent":       disk.Independent,
			"disk_id":           disk.ID,
			"shareable":         disk.Shareable,
			"size_used":         disk.SizeUsed,
			"size_max":          disk.SizeMax,
			"present_to":        disk.PresentTo,
			"storage_policy_id": disk.StoragePolicyID,
			"to_clean":          disk.ToClean,
			"permanently":       pernamentlyValue,
			"devicename":        disk.DeviceName,
			"create_by":         disk.CreatedBy,
			"create_time":       disk.CreatedTime,
			"delete_by":         disk.DeletedBy,
			"delete_time":       disk.DeletedTime,
			"update_time":       disk.UpdatedTime,
			"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 findInExtraDisks(diskId uint, extraDisks []interface{}) bool {
	for _, ExtraDisk := range extraDisks {
		if diskId == uint(ExtraDisk.(int)) {
			return true
		}
	}
	return false
}

func flattenAffinityRules(rules compute.ListRules) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(rules))

	for _, rule := range rules {
		res = append(res, map[string]interface{}{
			"topology": rule.Topology,
			"policy":   rule.Policy,
			"mode":     rule.Mode,
			"key":      rule.Key,
			"value":    rule.Value,
		})
	}

	return res
}

func flattenComputeList(computes *compute.ListComputes) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(computes.Data))
	for _, computeItem := range computes.Data {

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

	return res
}

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

func flattenListACLInterface(listAcl []interface{}) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(listAcl))
	for _, aclInterface := range listAcl {
		acl := aclInterface.(map[string]interface{})
		temp := map[string]interface{}{
			"explicit":      acl["explicit"],
			"guid":          acl["guid"],
			"right":         acl["right"],
			"status":        acl["status"],
			"type":          acl["type"],
			"user_group_id": acl["user_group_id"],
		}
		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 flattenListComputeACL(listAcl []compute.ItemComputeACL) []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 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 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 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 flattenComputeAudits(computeAudits compute.ListDetailedAudits) []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 flattenComputeGetAudits(computeAudits compute.ListAudits) []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 flattenPfwList(computePfws *compute.ListPFW) []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", flattenListComputeACL(userList.Data.ComputeACL))
	d.Set("rg_acl", flattenListACL(userList.Data.RGACL))
}

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

func flattenAffinityRelations(d *schema.ResourceData, ar *compute.RecordAffinityRelations) {
	d.Set("other_node", flattenNodes(ar.OtherNode))
	d.Set("other_node_indirect", flattenNodes(ar.OtherNodeIndirect))
	d.Set("other_node_indirect_soft", flattenNodes(ar.OtherNodeIndirectSoft))
	d.Set("other_node_soft", flattenNodes(ar.OtherNodeSoft))
	d.Set("same_node", flattenNodes(ar.SameNode))
	d.Set("same_node_soft", flattenNodes(ar.SameNodeSoft))
}

func flattenSnapshotUsage(computeSnapshotUsages compute.ListSnapshotUsage) []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 flattenPCIDevice(deviceList []compute.ItemPCIDevice) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(deviceList))
	for _, dev := range deviceList {
		temp := map[string]interface{}{
			"ckey":        dev.CKey,
			"meta":        flattens.FlattenMeta(dev.Meta),
			"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 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 flattenNodes(m []interface{}) []string {
	var output []string
	for _, item := range m {
		switch d := item.(type) {
		case string:
			output = append(output, d)
		case int:
			output = append(output, strconv.Itoa(d))
		case int64:
			output = append(output, strconv.FormatInt(d, 10))
		case float64:
			output = append(output, strconv.FormatInt(int64(d), 10))
		default:
			output = append(output, "")
		}
	}
	return output
}

func flattenDataCompute(d *schema.ResourceData, compFacts *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", compFacts.ID, compFacts.RGID)

	customFields, _ := json.Marshal(compFacts.CustomFields)
	devices, _ := json.Marshal(compFacts.Devices)
	userData, _ := json.Marshal(compFacts.Userdata)
	// general fields setting
	d.SetId(fmt.Sprintf("%d", compFacts.ID))
	d.Set("account_id", compFacts.AccountID)
	d.Set("account_name", compFacts.AccountName)
	d.Set("acl", flattenListACLInterface(compFacts.ACL))
	d.Set("affinity_label", compFacts.AffinityLabel)
	d.Set("affinity_rules", flattenAffinityRules(compFacts.AffinityRules))
	d.Set("affinity_weight", compFacts.AffinityWeight)
	d.Set("anti_affinity_rules", flattenAffinityRules(compFacts.AntiAffinityRules))
	d.Set("arch", compFacts.Arch)
	d.Set("auto_start_w_node", compFacts.AutoStart)
	d.Set("boot_order", compFacts.BootOrder)
	d.Set("boot_image_id", compFacts.ImageID)
	d.Set("chipset", compFacts.Chipset)
	d.Set("cd_image_id", compFacts.CdImageId)
	d.Set("clone_reference", compFacts.CloneReference)
	d.Set("clones", compFacts.Clones)
	d.Set("computeci_id", compFacts.ComputeCIID)
	d.Set("cpu_pin", compFacts.CPUPin)
	d.Set("cpus", compFacts.CPUs)
	d.Set("created_by", compFacts.CreatedBy)
	d.Set("created_time", compFacts.CreatedTime)
	d.Set("custom_fields", string(customFields))
	d.Set("deleted_by", compFacts.DeletedBy)
	d.Set("deleted_time", compFacts.DeletedTime)
	d.Set("desc", compFacts.Description)
	d.Set("devices", string(devices))
	d.Set("disks", flattenDisk(compFacts.Disks))
	d.Set("driver", compFacts.Driver)
	d.Set("gid", compFacts.GID)
	d.Set("guid", compFacts.GUID)
	d.Set("hp_backed", compFacts.HPBacked)
	d.Set("image_id", compFacts.ImageID)
	d.Set("image_name", compFacts.ImageName)
	d.Set("interfaces", flattenInterfaces(compFacts.Interfaces))
	d.Set("live_migration_job_id", compFacts.LiveMigrationJobID)
	d.Set("lock_status", compFacts.LockStatus)
	d.Set("manager_id", compFacts.ManagerID)
	d.Set("manager_type", compFacts.ManagerType)
	d.Set("migrationjob", compFacts.MigrationJob)
	d.Set("milestones", compFacts.Milestones)
	d.Set("name", compFacts.Name)
	d.Set("natable_vins_id", compFacts.NatableVINSID)
	d.Set("natable_vins_ip", compFacts.NatableVINSIP)
	d.Set("natable_vins_name", compFacts.NatableVINSName)
	d.Set("natable_vins_network", compFacts.NatableVINSNetwork)
	d.Set("natable_vins_network_name", compFacts.NatableVINSNetworkName)
	d.Set("need_reboot", compFacts.NeedReboot)
	d.Set("numa_affinity", compFacts.NumaAffinity)
	d.Set("numa_node_id", compFacts.NumaNodeId)
	d.Set("os_users", flattenOSUsers(compFacts.OSUsers))
	d.Set("pinned", compFacts.PinnedToNode)
	d.Set("preferred_cpu", compFacts.PreferredCPU)
	d.Set("qemu_guest", flattenQemuQuest(compFacts.QemuQuest))
	d.Set("ram", compFacts.RAM)
	d.Set("reference_id", compFacts.ReferenceID)
	d.Set("registered", compFacts.Registered)
	d.Set("res_name", compFacts.ResName)
	d.Set("reserved_node_cpus", compFacts.ReservedNodeCpus)
	d.Set("rg_id", compFacts.RGID)
	d.Set("rg_name", compFacts.RGName)
	d.Set("snap_sets", flattenSnapSets(compFacts.SnapSets))
	d.Set("node_id", compFacts.NodeID)
	d.Set("node_name", compFacts.NodeName)
	d.Set("stateless_sep_id", compFacts.StatelessSEPID)
	d.Set("stateless_sep_type", compFacts.StatelessSEPType)
	d.Set("status", compFacts.Status)
	d.Set("tags", flattenTags(compFacts.Tags))
	d.Set("tech_status", compFacts.TechStatus)
	d.Set("updated_by", compFacts.UpdatedBy)
	d.Set("updated_time", compFacts.UpdatedTime)
	d.Set("user_data", string(userData))
	d.Set("user_managed", compFacts.UserManaged)
	d.Set("read_only", compFacts.ReadOnly)
	d.Set("vnc_password", compFacts.VNCPassword)
	d.Set("vgpus", flattenVGPUs(compFacts.VGPUs))
	d.Set("pci_devices", flattenPCI(*pciList))
	d.Set("loader_type", compFacts.LoaderType)
	d.Set("boot_type", compFacts.BootType)
	d.Set("hot_resize", compFacts.HotResize)
	d.Set("network_interface_naming", compFacts.NetworkInterfaceNaming)
	d.Set("zone_id", compFacts.ZoneID)
	d.Set("loader_meta_iso", flattenLoaderMetaIso(compFacts.LoaderMetaIso))
	d.Set("os_version", compFacts.OSVersion)
	d.Set("weight", compFacts.Weight)
	//extra fields setting
	bootDisk := findBootDisk(compFacts.Disks, compFacts.Chipset)
	if bootDisk != nil {
		d.Set("boot_disk_size", bootDisk.SizeMax)
		d.Set("boot_disk_id", bootDisk.ID) // we may need boot disk ID in resize operations
		d.Set("sep_id", bootDisk.SEPID)
		d.Set("pool", bootDisk.Pool)
	}

	return nil
}

// Parse the list of interfaces from compute/get response into a list of networks
// attached to this compute
func parseComputeInterfacesToNetworks(networks []interface{}, ifaces compute.ListInterfaces) []interface{} {
	// return value will be used to d.Set("network") item of dataSourceCompute schema
	length := len(ifaces)
	log.Debugf("parseComputeInterfacesToNetworks: called for %d ifaces", length)

	result := []interface{}{}
	for _, value := range ifaces {
		elem := make(map[string]interface{})
		// Keys in this map should correspond to the Schema definition for "network"
		elem["net_id"] = value.NetID
		elem["net_type"] = value.NetType
		elem["ip_address"] = value.IPAddress
		elem["mac"] = value.MAC
		elem["mtu"] = value.MTU
		elem["net_mask"] = value.NetMask
		elem["sdn_interface_id"] = value.SDNInterfaceID
		elem["weight"] = flattenNetworkWeight(networks, value.NetID, value.NetType)
		elem["enabled"] = value.Enabled

		result = append(result, elem)
	}

	return result
}

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 flattenDisk(diskList compute.ListDisks) []map[string]interface{} {
	res := make([]map[string]interface{}, 0, len(diskList))
	for _, disk := range diskList {
		temp := map[string]interface{}{
			"ckey":                  disk.CKey,
			"meta":                  flattens.FlattenMeta(disk.Meta),
			"account_id":            disk.AccountID,
			"blk_discard":           disk.BLKDiscard,
			"block_size":            disk.BlockSize,
			"boot_partition":        disk.BootPartition,
			"bus_number":            disk.BusNumber,
			"created_time":          disk.CreatedTime,
			"created_by":            disk.CreatedBy,
			"deleted_time":          disk.DeletedTime,
			"deleted_by":            disk.DeletedBy,
			"devicename":            disk.DeviceName,
			"desc":                  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,
			"independent":           disk.Independent,
			"iotune":                flattenIOTune(disk.IOTune),
			"iqn":                   disk.IQN,
			"login":                 disk.Login,
			"milestones":            disk.Milestones,
			"name":                  disk.Name,
			"params":                disk.Params,
			"parent_id":             disk.ParentID,
			"passwd":                disk.Password,
			"pci_slot":              disk.PCISlot,
			"pool":                  disk.Pool,
			"purge_attempts":        disk.PurgeAttempts,
			"present_to":            disk.PresentTo,
			"purge_time":            disk.PurgeTime,
			"replication":           flattenDiskReplication(disk.Replication),
			"reality_device_number": disk.RealityDeviceNumber,
			"reference_id":          disk.ReferenceID,
			"res_id":                disk.ResID,
			"res_name":              disk.ResName,
			"role":                  disk.Role,
			"sep_id":                disk.SEPID,
			"shareable":             disk.Shareable,
			"size_available":        disk.SizeAvailable,
			"size_max":              disk.SizeMax,
			"size_used":             disk.SizeUsed,
			"snapshots":             flattendDiskSnapshotList(disk.Snapshots),
			"status":                disk.Status,
			"storage_policy_id":     disk.StoragePolicyID,
			"tech_status":           disk.TechStatus,
			"type":                  disk.Type,
			"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 flattenIOTune(iot compute.IOTune) []map[string]interface{} {
	res := make([]map[string]interface{}, 0)
	temp := map[string]interface{}{
		"read_bytes_sec":      iot.ReadBytesSec,
		"read_bytes_sec_max":  iot.ReadBytesSecMax,
		"read_iops_sec":       iot.ReadIOPSSec,
		"read_iops_sec_max":   iot.ReadIOPSSecMax,
		"size_iops_sec":       iot.SizeIOPSSec,
		"total_bytes_sec":     iot.TotalBytesSec,
		"total_bytes_sec_max": iot.TotalBytesSecMax,
		"total_iops_sec":      iot.TotalIOPSSec,
		"total_iops_sec_max":  iot.TotalIOPSSecMax,
		"write_bytes_sec":     iot.WriteBytesSec,
		"write_bytes_sec_max": iot.WriteBytesSecMax,
		"write_iops_sec":      iot.WriteIOPSSec,
		"write_iops_sec_max":  iot.WriteIOPSSecMax,
	}

	res = append(res, temp)
	return res
}

func flattendDiskSnapshotList(sl compute.ListDetailedSnapshots) []interface{} {
	res := make([]interface{}, 0)
	for _, snapshot := range sl {
		temp := map[string]interface{}{
			"guid":          snapshot.GUID,
			"label":         snapshot.Label,
			"res_id":        snapshot.ResID,
			"snap_set_guid": snapshot.SnapSetGUID,
			"snap_set_time": snapshot.SnapSetTime,
			"timestamp":     snapshot.TimeStamp,
		}
		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
}
