package flattens

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-log/tflog"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/compute"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/flattens"
	disks "repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/disks/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/kvmvm/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/kvmvm/utilities"
)

func ComputeResource(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start flattens.ComputeResource")

	diags := diag.Diagnostics{}

	recordItemCompute, diags := utilities.ComputeResourceCheckPresence(ctx, plan, c)
	if diags.HasError() {
		return diags
	}
	pfwList, err := utilities.ComputeResourcePFWListGet(ctx, recordItemCompute.ID, c)
	if err != nil {
		if err != nil {
			diags.AddError("ComputeResourcePFWListGet error: ", err.Error())
			tflog.Error(ctx, fmt.Sprint("ComputeResourcePFWListGet error ", err))
		}
	}

	userList, err := utilities.ComputeResourceUserAccessGet(ctx, recordItemCompute.ID, c)
	if err != nil {
		tflog.Error(ctx, fmt.Sprintf("Error getting user list: %v", err))
	}

	pciDevicesList, err := utilities.ResourceComputePCIDevicesListCheckPresence(ctx, recordItemCompute.ID, c)
	if err != nil {
		tflog.Error(ctx, fmt.Sprintf("Error getting pci list: %v", err))
	}

	bootdisk := findBootDisk(recordItemCompute.Disks)
	devices, _ := json.Marshal(recordItemCompute.Devices)
	userdata, _ := json.Marshal(recordItemCompute.Userdata)
	customFields, _ := json.Marshal(recordItemCompute.CustomFields)

	*plan = models.ResourceComputeModel{
		// required fields
		Name:     types.StringValue(recordItemCompute.Name),
		RGID:     types.Int64Value(int64(recordItemCompute.RGID)),
		Driver:   types.StringValue(recordItemCompute.Driver),
		CPU:      types.Int64Value(int64(recordItemCompute.CPU)),
		RAM:      types.Int64Value(int64(recordItemCompute.RAM)),
		Timeouts: plan.Timeouts,

		// optional fields
		ImageID:         types.Int64Value(int64(bootdisk.ImageID)),
		WithoutBootDisk: plan.WithoutBootDisk,
		// we intentionally use the SizeMax field, do not change it until the BootDiskSize field is fixed on the platform
		BootDiskSize:        types.Int64Value(int64(bootdisk.SizeMax)),
		AffinityLabel:       types.StringValue(recordItemCompute.AffinityLabel),
		AffinityRules:       flattenComputeAffinityRule(ctx, &recordItemCompute.AffinityRules),
		AntiAffinityRules:   flattenComputeAffinityRule(ctx, &recordItemCompute.AntiAffinityRules),
		AutoStartWithNode:   types.BoolValue(recordItemCompute.AutoStart),
		CustomFields:        types.StringValue(string(customFields)),
		Chipset:             types.StringValue(recordItemCompute.Chipset),
		SepId:               types.Int64Value(int64(bootdisk.SepID)),
		Pool:                types.StringValue(bootdisk.Pool),
		ExtraDisks:          plan.ExtraDisks,
		Network:             flattenNetwork(ctx, plan.Network, &recordItemCompute.Interfaces),
		Tags:                flattenComputeTags(ctx, recordItemCompute.Tags),
		PortForwarding:      flattenComputePortForwarding(ctx, pfwList.Data),
		UserAccess:          flattenUserAccess(ctx, userList),
		Snapshot:            plan.Snapshot,
		PCIDevices:          flattens.FlattenSimpleTypeToSet(ctx, types.Int64Type, flattenPCIDevices(ctx, pciDevicesList)),
		Rollback:            plan.Rollback,
		CDImageID:           types.Int64Value(int64(recordItemCompute.CdImageId)),
		PinToStack:          types.BoolValue(recordItemCompute.PinnedToStack),
		Description:         types.StringValue(recordItemCompute.Description),
		CloudInit:           plan.CloudInit,
		Enabled:             types.BoolValue(recordItemCompute.Status == "ENABLED"),
		Pause:               types.BoolValue(recordItemCompute.TechStatus == "PAUSED"),
		Reset:               plan.Reset,
		Restore:             plan.Restore,
		ForceStop:           plan.ForceStop,
		ForceResize:         plan.ForceResize,
		Started:             types.BoolValue(recordItemCompute.TechStatus == "STARTED"),
		DetachDisks:         plan.DetachDisks,
		Permanently:         plan.Permanently,
		IS:                  plan.IS,
		IpaType:             plan.IpaType,
		NumaAffinity:        types.StringValue(recordItemCompute.NumaAffinity),
		CPUPin:              types.BoolValue(recordItemCompute.CPUPin),
		HPBacked:            types.BoolValue(recordItemCompute.HPBacked),
		SnapshotDeleteAsync: plan.SnapshotDeleteAsync,

		LoaderType:             types.StringValue(recordItemCompute.LoaderType),
		BootType:               types.StringValue(recordItemCompute.BootType),
		HotResize:              types.BoolValue(recordItemCompute.HotResize),
		NetworkInterfaceNaming: types.StringValue(recordItemCompute.NetworkInterfaceNaming),
		//computed fields
		AccountId:              types.Int64Value(int64(recordItemCompute.AccountID)),
		AccountName:            types.StringValue(recordItemCompute.AccountName),
		ACL:                    flattenResourceACL(ctx, &recordItemCompute.ACL),
		AffinityWeight:         types.Int64Value(int64(recordItemCompute.AffinityWeight)),
		Architecture:           types.StringValue(recordItemCompute.Architecture),
		BootOrder:              flattens.FlattenSimpleTypeToList(ctx, types.StringType, recordItemCompute.BootOrder),
		BootDisk:               flattenDisk(ctx, bootdisk),
		BootDiskId:             types.Int64Value(int64(bootdisk.ID)),
		CloneReference:         types.Int64Value(int64(recordItemCompute.CloneReference)),
		Clones:                 flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordItemCompute.Clones),
		ComputeCIID:            types.Int64Value(int64(recordItemCompute.ComputeCIID)),
		ComputeId:              types.Int64Value(int64(recordItemCompute.ID)),
		CreatedBy:              types.StringValue(recordItemCompute.CreatedBy),
		CreatedTime:            types.Int64Value(int64(recordItemCompute.CreatedTime)),
		DeletedBy:              types.StringValue(recordItemCompute.DeletedBy),
		DeletedTime:            types.Int64Value(int64(recordItemCompute.DeletedTime)),
		Devices:                types.StringValue(string(devices)),
		Disks:                  flattenResourceDisks(ctx, &recordItemCompute.Disks),
		GID:                    types.Int64Value(int64(recordItemCompute.GID)),
		GUID:                   types.Int64Value(int64(recordItemCompute.GUID)),
		ID:                     plan.ID,
		ImageName:              types.StringValue(recordItemCompute.ImageName),
		Interfaces:             flattenResourceInterfaces(ctx, &recordItemCompute.Interfaces),
		LockStatus:             types.StringValue(recordItemCompute.LockStatus),
		ManagerID:              types.Int64Value(int64(recordItemCompute.ManagerID)),
		ManagerType:            types.StringValue(recordItemCompute.ManagerType),
		MigrationJob:           types.Int64Value(int64(recordItemCompute.MigrationJob)),
		Milestones:             types.Int64Value(int64(recordItemCompute.Milestones)),
		NatableVINSID:          types.Int64Value(int64(recordItemCompute.NatableVINSID)),
		NatableVINSIP:          types.StringValue(recordItemCompute.NatableVINSIP),
		NatableVINSName:        types.StringValue(recordItemCompute.NatableVINSName),
		NatableVINSNetwork:     types.StringValue(recordItemCompute.NatableVINSNetwork),
		NatableVINSNetworkName: types.StringValue(recordItemCompute.NatableVINSNetworkName),
		NeedReboot:             types.BoolValue(recordItemCompute.NeedReboot),
		NumaNodeId:             types.Int64Value(int64(recordItemCompute.NumaNodeId)),
		OSUsers:                flattenResourceOSUsers(ctx, &recordItemCompute.OSUsers),
		Pinned:                 types.BoolValue(recordItemCompute.PinnedToStack),
		PreferredCPU:           flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordItemCompute.PreferredCPU),
		ReferenceID:            types.StringValue(recordItemCompute.ReferenceID),
		Registered:             types.BoolValue(recordItemCompute.Registered),
		ResName:                types.StringValue(recordItemCompute.ResName),
		ReservedNodeCpus:       flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, recordItemCompute.ReservedNodeCpus),
		RGName:                 types.StringValue(recordItemCompute.RGName),
		SnapSets:               flattenSnapSets(ctx, &recordItemCompute.SnapSets),
		StatelessSepID:         types.Int64Value(int64(recordItemCompute.StatelessSepID)),
		StatelessSepType:       types.StringValue(recordItemCompute.StatelessSepType),
		Status:                 types.StringValue(recordItemCompute.Status),
		TechStatus:             types.StringValue(recordItemCompute.TechStatus),
		UpdatedBy:              types.StringValue(recordItemCompute.UpdatedBy),
		UpdatedTime:            types.Int64Value(int64(recordItemCompute.UpdatedTime)),
		UserManaged:            types.BoolValue(recordItemCompute.UserManaged),
		Userdata:               types.StringValue(string(userdata)),
		VGPUs:                  flattenResourceComputeVGPU(ctx, recordItemCompute.VGPUs),
		VirtualImageID:         types.Int64Value(int64(recordItemCompute.VirtualImageID)),
		VirtualImageName:       types.StringValue(recordItemCompute.VirtualImageName),
		VNCPassword:            types.StringValue(recordItemCompute.VNCPassword),
	}

	tflog.Info(ctx, "End flattens.ComputeResource", map[string]any{"id": plan.ID.ValueString()})
	return nil
}

func findBootDisk(disks compute.ListComputeDisks) *compute.ItemComputeDisk {
	for _, disk := range disks {
		if disk.Type == "B" {
			return &disk
		}
	}
	return &compute.ItemComputeDisk{}
}

func flattenResourceACL(ctx context.Context, acl *compute.RecordACL) types.Object {
	tflog.Info(ctx, "Start flattenACL")

	temp := models.RecordResourceACLModel{
		AccountACL: flattenACLItems(ctx, &acl.AccountACL),
		ComputeACL: flattenACLItems(ctx, &acl.ComputeACL),
		RGACL:      flattenACLItems(ctx, &acl.RGACL),
	}

	res, err := types.ObjectValueFrom(ctx, models.ListACL, temp)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenACL struct to obj", err))
	}

	tflog.Info(ctx, "End flattenACL")
	return res
}

func flattenACLItems(ctx context.Context, item *compute.ListACL) types.List {
	tflog.Info(ctx, "Start flattenACLItems")
	tempSlice := make([]types.Object, 0, len(*item))
	for _, aclItem := range *item {
		temp := models.ItemACLModel{
			Explicit:    types.BoolValue(bool(aclItem.Explicit)),
			GUID:        types.StringValue(aclItem.GUID),
			Right:       types.StringValue(aclItem.Right),
			Status:      types.StringValue(aclItem.Status),
			Type:        types.StringValue(aclItem.Type),
			UserGroupID: types.StringValue(aclItem.UserGroupID),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemACL, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenACLItems struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemACL}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenACLItems", err))
	}

	tflog.Info(ctx, "End flattenACLItems")
	return res
}

func flattenDisk(ctx context.Context, disk *compute.ItemComputeDisk) types.Object {
	tflog.Info(ctx, fmt.Sprintf("flattenDisk: start flatten disk with ID - %v", disk.ID))

	diags := diag.Diagnostics{}

	acl, _ := json.Marshal(disk.ACL)
	temp := models.ItemResourceDiskModel{
		ACL:                 types.StringValue(string(acl)),
		AccountID:           types.Int64Value(int64(disk.AccountID)),
		BootPartition:       types.Int64Value(int64(disk.BootPartition)),
		BusNumber:           types.Int64Value(int64(disk.BusNumber)),
		CreatedTime:         types.Int64Value(int64(disk.CreatedTime)),
		DeletedTime:         types.Int64Value(int64(disk.DeletedTime)),
		Description:         types.StringValue(disk.Description),
		DestructionTime:     types.Int64Value(int64(disk.DestructionTime)),
		DiskPath:            types.StringValue(disk.DiskPath),
		GID:                 types.Int64Value(int64(disk.GID)),
		GUID:                types.Int64Value(int64(disk.GUID)),
		ID:                  types.Int64Value(int64(disk.ID)),
		ImageID:             types.Int64Value(int64(disk.ImageID)),
		Images:              flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, disk.Images),
		IOTune:              flattensIOTune(ctx, &disk.IOTune),
		IQN:                 types.StringValue(disk.IQN),
		Login:               types.StringValue(disk.Login),
		Milestones:          types.Int64Value(int64(disk.Milestones)),
		Name:                types.StringValue(disk.Name),
		Order:               types.Int64Value(int64(disk.Order)),
		Params:              types.StringValue(disk.Params),
		ParentID:            types.Int64Value(int64(disk.ParentID)),
		Passwd:              types.StringValue(disk.Passwd),
		Pool:                types.StringValue(disk.Pool),
		PCISlot:             types.Int64Value(disk.PCISlot),
		PurgeTime:           types.Int64Value(int64(disk.PurgeTime)),
		RealityDeviceNumber: types.Int64Value(int64(disk.RealityDeviceNumber)),
		Replication:         flattenDiskReplication(ctx, &disk.Replication),
		ResID:               types.StringValue(disk.ResID),
		Role:                types.StringValue(disk.Role),
		SepID:               types.Int64Value(int64(disk.SepID)),
		Shareable:           types.BoolValue(disk.Shareable),
		SizeMax:             types.Int64Value(int64(disk.SizeMax)),
		SizeUsed:            types.Float64Value(disk.SizeUsed),
		Snapshots:           flattenResourceSnapshotExtend(ctx, &disk.Snapshots),
		Status:              types.StringValue(disk.Status),
		TechStatus:          types.StringValue(disk.TechStatus),
		Type:                types.StringValue(disk.Type),
		VMID:                types.Int64Value(int64(disk.VMID)),
	}

	temp.PresentTo, diags = types.MapValueFrom(ctx, types.Int64Type, disk.PresentTo)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenPresentTo", diags))
	}

	res, diags := types.ObjectValueFrom(ctx, models.ItemDisk, temp)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenDisk struct to obj", diags))
	}

	tflog.Info(ctx, fmt.Sprintf("flattenDisk: end flatten disk with ID - %v", disk.ID))
	return res
}

func flattensIOTune(ctx context.Context, ioTune *compute.IOTune) types.Object {
	tflog.Info(ctx, "Start flattensIOTune")
	temp := models.IOTuneModel{
		ReadBytesSec:     types.Int64Value(int64(ioTune.ReadBytesSec)),
		ReadBytesSecMax:  types.Int64Value(int64(ioTune.ReadBytesSecMax)),
		ReadIOPSSec:      types.Int64Value(int64(ioTune.ReadIOPSSec)),
		ReadIOPSSecMax:   types.Int64Value(int64(ioTune.ReadIOPSSecMax)),
		SizeIOPSSec:      types.Int64Value(int64(ioTune.SizeIOPSSec)),
		TotalBytesSec:    types.Int64Value(int64(ioTune.TotalBytesSec)),
		TotalBytesSecMax: types.Int64Value(int64(ioTune.TotalBytesSecMax)),
		TotalIOPSSec:     types.Int64Value(int64(ioTune.TotalIOPSSec)),
		TotalIOPSSecMax:  types.Int64Value(int64(ioTune.TotalIOPSSecMax)),
		WriteBytesSec:    types.Int64Value(int64(ioTune.WriteBytesSec)),
		WriteBytesSecMax: types.Int64Value(int64(ioTune.WriteBytesSecMax)),
		WriteIOPSSec:     types.Int64Value(int64(ioTune.WriteIOPSSec)),
		WriteIOPSSecMax:  types.Int64Value(int64(ioTune.WriteIOPSSecMax)),
	}

	res, err := types.ObjectValueFrom(ctx, disks.ItemIOTune, temp)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattensIOTune struct to obj", err))
	}

	tflog.Info(ctx, "End flattensIOTune")
	return res
}

func flattenDiskReplication(ctx context.Context, replication *compute.ItemReplication) types.Object {
	tflog.Info(ctx, "Start flattenDiskReplication")
	temp := models.ReplicationModel{
		DiskID:       types.Int64Value(int64(replication.DiskID)),
		PoolID:       types.StringValue(replication.PoolID),
		Role:         types.StringValue(replication.Role),
		SelfVolumeID: types.StringValue(replication.SelfVolumeID),
		StorageID:    types.StringValue(replication.StorageID),
		VolumeID:     types.StringValue(replication.VolumeID),
	}

	res, err := types.ObjectValueFrom(ctx, models.ItemReplication, temp)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenDiskReplication struct to obj", err))
	}

	tflog.Info(ctx, "End flattenDiskReplication")
	return res
}

func flattenResourceSnapshotExtend(ctx context.Context, snapshotList *compute.SnapshotExtendList) types.List {
	tflog.Info(ctx, "Start flattenResourceSnapshotExtend")
	tempSlice := make([]types.Object, 0, len(*snapshotList))
	for _, snapshot := range *snapshotList {
		temp := models.ItemSnapshotExtendModel{
			GUID:        types.StringValue(snapshot.GUID),
			Label:       types.StringValue(snapshot.Label),
			ReferenceID: types.StringValue(snapshot.ReferenceID),
			ResID:       types.StringValue(snapshot.ResID),
			SnapSetGUID: types.StringValue(snapshot.SnapSetGUID),
			SnapSetTime: types.Int64Value(int64(snapshot.SnapSetTime)),
			TimeStamp:   types.Int64Value(int64(snapshot.TimeStamp)),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemSnapshot, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenResourceSnapshotExtend struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemSnapshot}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenResourceSnapshotExtend", err))
	}

	tflog.Info(ctx, "End flattenResourceSnapshotExtend")
	return res
}

func flattenResourceOSUsers(ctx context.Context, osUsersList *compute.ListOSUser) types.List {
	tflog.Info(ctx, "Start flattensOSUsers")
	tempSlice := make([]types.Object, 0, len(*osUsersList))
	for _, osUser := range *osUsersList {
		temp := models.ItemOSUserModel{
			GUID:     types.StringValue(osUser.GUID),
			Login:    types.StringValue(osUser.Login),
			Password: types.StringValue(osUser.Password),
			PubKey:   types.StringValue(osUser.PubKey),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemOSUsers, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattensOSUsers struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemOSUsers}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattensOSUsers", err))
	}

	tflog.Info(ctx, "End flattensOSUsers")
	return res
}

func flattenResourceDisks(ctx context.Context, disksList *compute.ListComputeDisks) types.List {
	tflog.Info(ctx, "Start flattenResourceDisks")
	tempSlice := make([]types.Object, 0, len(*disksList))
	for _, disk := range *disksList {
		if disk.Type == "B" {
			continue
		}
		temp := flattenDisk(ctx, &disk)
		obj, err := types.ObjectValueFrom(ctx, models.ItemDisk, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenResourceDisks struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemDisk}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenResourceDisks", err))
	}

	tflog.Info(ctx, "End flattenResourceDisks")
	return res
}

func flattenResourceInterfaces(ctx context.Context, interfaces *compute.ListInterfaces) types.List {
	tflog.Info(ctx, "Start flattenResourceInterfaces")

	tempSlice := make([]types.Object, 0, len(*interfaces))

	for _, item := range *interfaces {
		temp := models.ItemResourceInterfacesModel{
			BusNumber:       types.Int64Value(int64(item.BusNumber)),
			ConnID:          types.Int64Value(int64(item.ConnID)),
			ConnType:        types.StringValue(item.ConnType),
			GetGW:           types.StringValue(item.DefGW),
			Enabled:         types.BoolValue(item.Enabled),
			FLIPGroupID:     types.Int64Value(int64(item.FLIPGroupID)),
			GUID:            types.StringValue(item.GUID),
			IPAddress:       types.StringValue(item.IPAddress),
			ListenSSH:       types.BoolValue(item.ListenSSH),
			MAC:             types.StringValue(item.MAC),
			MTU:             types.Int64Value(int64(item.MTU)),
			Name:            types.StringValue(item.Name),
			NetID:           types.Int64Value(int64(item.NetID)),
			NetMask:         types.Int64Value(int64(item.NetMask)),
			NetType:         types.StringValue(item.NetType),
			NodeID:          types.Int64Value(int64(item.NodeID)),
			PCISlot:         types.Int64Value(item.PCISlot),
			QOS:             flattenQOS(ctx, &item.QOS),
			LibvirtSettings: flattenLibvirtSetttings(ctx, &item.LibvirtSettings),
			Target:          types.StringValue(item.Target),
			Type:            types.StringValue(item.Type),
			VNFs:            flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, item.VNFs),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemInterfaces, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenResourceInterfaces struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemInterfaces}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenResourceInterfaces", err))
	}

	tflog.Info(ctx, "End flattenInterfaces")
	return res
}

func flattenQOS(ctx context.Context, QOS *compute.QOS) types.Object {
	tflog.Info(ctx, "Start flattenQOS")
	temp := models.QOSModel{
		ERate:   types.Int64Value(int64(QOS.ERate)),
		GUID:    types.StringValue(QOS.GUID),
		InBurst: types.Int64Value(int64(QOS.InBurst)),
		InRate:  types.Int64Value(int64(QOS.InRate)),
	}

	res, err := types.ObjectValueFrom(ctx, models.ItemQos, temp)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenQOS struct to obj", err))
	}

	tflog.Info(ctx, "End flattenQOS")
	return res
}

func flattenLibvirtSetttings(ctx context.Context, settings *compute.LibvirtSettings) types.Object {
	tflog.Info(ctx, "Start flattenLibvirtSetttings")
	temp := models.LibvirtModel{
		GUID:        types.StringValue(settings.GUID),
		TXMode:      types.StringValue(settings.TXMode),
		IOEventFD:   types.StringValue(settings.IOEventFD),
		EventIDx:    types.StringValue(settings.EventIDx),
		Queues:      types.Int64Value(int64(settings.Queues)),
		RXQueueSize: types.Int64Value(int64(settings.RXQueueSize)),
		TXQueueSize: types.Int64Value(int64(settings.TXQueueSize)),
	}

	res, err := types.ObjectValueFrom(ctx, models.ItemLibvirtSettings, temp)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenLibvirtSetttings struct to obj", err))
	}

	tflog.Info(ctx, "End flattenLibvirtSetttings")
	return res
}

func flattenSnapSets(ctx context.Context, snapSets *compute.ListSnapSets) types.List {
	tflog.Info(ctx, "Start flattenSnapSets")
	tempSlice := make([]types.Object, 0, len(*snapSets))
	for _, snapSet := range *snapSets {
		temp := models.ItemSnapSetModel{
			Disks:     flattens.FlattenSimpleTypeToList(ctx, types.Int64Type, snapSet.Disks),
			GUID:      types.StringValue(snapSet.GUID),
			Label:     types.StringValue(snapSet.Label),
			Timestamp: types.Int64Value(int64(snapSet.Timestamp)),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemSnapSets, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenSnapSets struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemSnapSets}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenSnapSets", err))
	}

	tflog.Info(ctx, "End flattenSnapSets")
	return res
}

func flattenNetwork(ctx context.Context, networks types.Set, interfaces *compute.ListInterfaces) types.Set {
	tflog.Info(ctx, "Start flattenNetwork")

	tempSlice := make([]types.Object, 0, len(*interfaces))

	for _, item := range *interfaces {
		temp := models.ItemNetworkModel{
			NetType:   types.StringValue(item.NetType),
			NetId:     types.Int64Value(int64(item.NetID)),
			IpAddress: types.StringValue(item.IPAddress),
			Mac:       types.StringValue(item.MAC),
			Weight:    flattenNetworkWeight(ctx, networks, item),
			MTU:       types.Int64Value(int64(item.MTU)),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemNetwork, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenNetwork struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemNetwork}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenNetwork", err))
	}

	tflog.Info(ctx, "End flattenNetwork")
	return res
}

func flattenNetworkWeight(ctx context.Context, networks types.Set, item compute.ItemVNFInterface) types.Int64 {
	tflog.Info(ctx, "Start flattenNetworkWeight")
	networkList := networks.Elements()
	for _, network := range networkList {
		networkMap := network.(types.Object).Attributes()
		if uint64(networkMap["net_id"].(types.Int64).ValueInt64()) == item.NetID && networkMap["net_type"].(types.String).ValueString() == item.NetType {
			return types.Int64Value(networkMap["weight"].(types.Int64).ValueInt64())
		}
	}
	tflog.Info(ctx, "End flattenNetworkWeight")
	return types.Int64Value(0)
}

func flattenComputeAffinityRule(ctx context.Context, rules *compute.ListRules) types.Set {
	tflog.Info(ctx, "Start flattenAffinityRule")

	tempSlice := make([]types.Object, 0, len(*rules))
	for _, item := range *rules {
		temp := models.ItemResourceRulesModel{
			Key:      types.StringValue(item.Key),
			Mode:     types.StringValue(item.Mode),
			Policy:   types.StringValue(item.Policy),
			Topology: types.StringValue(item.Topology),
			Value:    types.StringValue(item.Value),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemResourceRules, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenComputeAffinityRule struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemResourceRules}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenComputeAffinityRule", err))
	}

	tflog.Info(ctx, "End flattenAffinityRule")
	return res
}

func flattenComputeTags(ctx context.Context, tags map[string]string) types.Set {
	tflog.Info(ctx, "Start flattenComputeTags")

	tempSlice := make([]types.Object, 0, len(tags))
	for key, value := range tags {
		temp := models.ItemTagModel{
			Key:   types.StringValue(key),
			Value: types.StringValue(value),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemTag, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenComputeTags struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemTag}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenComputeTags", err))
	}

	tflog.Info(ctx, "End flattenComputeTags")
	return res
}

func flattenComputePortForwarding(ctx context.Context, list []compute.ItemPFW) types.Set {
	tflog.Info(ctx, "Start flattenComputePortForwarding")

	tempSlice := make([]types.Object, 0, len(list))
	for _, item := range list {
		temp := models.ItemPortForfardingModel{
			PublicPortStart: types.Int64Value(int64(item.PublicPortStart)),
			PublicPortEnd:   types.Int64Value(int64(item.PublicPortEnd)),
			LocalPort:       types.Int64Value(int64(item.LocalPort)),
			Proto:           types.StringValue(item.Protocol),
		}
		obj, err := types.ObjectValueFrom(ctx, models.ItemPortForfarding, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenComputePortForwarding struct to obj", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemPortForfarding}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenComputePortForwarding", err))
	}

	tflog.Info(ctx, "End flattenComputePortForwarding")
	return res
}

func flattenUserAccess(ctx context.Context, userList *compute.ListUsers) types.Set {
	tflog.Info(ctx, "Start flattenUserAccess")
	tempSlice := make([]types.Object, 0)

	// Process compute ACL
	for _, item := range userList.Data.ComputeACL {
		temp := models.UserAccessModel{
			Username:   types.StringValue(item.UserGroupID),
			AccessType: types.StringValue(item.Right),
		}
		obj, diags := types.ObjectValueFrom(ctx, models.ItemUserAccess, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenUserAccess struct to obj", diags))
			continue
		}
		tempSlice = append(tempSlice, obj)
	}

	res, diags := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemUserAccess}, tempSlice)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenUserAccess", diags))
	}

	tflog.Info(ctx, "End flattenUserAccess")
	return res
}

func flattenPCIDevices(ctx context.Context, pciList *compute.ListPCIDevices) []uint64 {
	tflog.Info(ctx, "Start flattenPCI")
	res := make([]uint64, 0, len(pciList.Data))

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

	tflog.Info(ctx, "End flattenPCI")
	return res
}

func flattenResourceComputeVGPU(ctx context.Context, vgpuList []compute.VGPUItem) types.List {
	tflog.Info(ctx, "Start flattenVGPU")
	tempSlice := make([]types.Object, 0, len(vgpuList))

	for _, item := range vgpuList {
		temp := models.ComputeVGPUModel{
			ID:             types.Int64Value(int64(item.ID)),
			GID:            types.Int64Value(int64(item.GID)),
			Type:           types.StringValue(item.Type),
			Mode:           types.StringValue(item.Mode),
			Status:         types.StringValue(item.Status),
			ProfileID:      types.Int64Value(int64(item.ProfileID)),
			RAM:            types.Int64Value(int64(item.RAM)),
			LastUpdateTime: types.Int64Value(int64(item.LastUpdateTime)),
			CreatedTime:    types.Int64Value(int64(item.CreatedTime)),
			DeletedTime:    types.Int64Value(int64(item.DeletedTime)),
			VMID:           types.Int64Value(int64(item.VMID)),
			PGPUID:         types.Int64Value(int64(item.PGPuid)),
			ReferenceID:    types.StringValue(item.ReferenceID),
			AccountID:      types.Int64Value(int64(item.AccountID)),
			RGID:           types.Int64Value(int64(item.RgID)),
			LastClaimedBy:  types.Int64Value(int64(item.LastClaimedBy)),
			PCISlot:        types.Int64Value(int64(item.PCISlot)),
			BusNumber:      types.Int64Value(int64(item.BusNumber)),
			GUID:           types.Int64Value(int64(item.GUID)),
		}

		obj, err := types.ObjectValueFrom(ctx, models.ComputeItemVGPU, temp)
		if err != nil {
			tflog.Error(ctx, fmt.Sprint("Error converting VGPU model to object", err))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, err := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ComputeItemVGPU}, tempSlice)
	if err != nil {
		tflog.Error(ctx, fmt.Sprint("Error creating VGPU list value", err))
	}

	tflog.Info(ctx, "End flattenVGPU")
	return res
}
