package utilities

import (
	"context"
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform-plugin-framework/attr"
	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
	"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/service/cloudapi/ic"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/kvmvm/models"
)

func ComputeResourceResize(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceResize: start resize compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceResize: cannot parsed ID compute from state", err.Error())
		return diags
	}

	resizeReq := compute.ResizeRequest{
		ComputeID: computeId,
		CPU:       0,
		RAM:       0,
		Force:     false,
	}

	if plan.ForceResize.ValueBool() {
		resizeReq.Force = true
	}

	if !plan.CPU.Equal(state.CPU) && (state.CPU.ValueInt64() > plan.CPU.ValueInt64()) && !resizeReq.Force {
		diags.AddError(fmt.Sprintf("Cannot resize compute ID %d: enable 'force_resize' to reduce compute vCPUs", computeId), "")
		return diags
	}
	if !plan.CPU.Equal(state.CPU) {
		resizeReq.CPU = uint64(plan.CPU.ValueInt64())
	}

	if resizeReq.CPU != 0 {
		if !plan.PreferredCPU.IsUnknown() {
			preferredCPUList := plan.PreferredCPU.Elements()
			preferredCPU := make([]int64, 0, len(preferredCPUList))
			for _, elem := range preferredCPUList {
				preferredCPU = append(preferredCPU, elem.(types.Int64).ValueInt64())
			}
			resizeReq.PreferredCPU = preferredCPU
		}
		if len(state.PreferredCPU.Elements()) != 0 && len(resizeReq.PreferredCPU) == 0 {
			resizeReq.PreferredCPU = []int64{-1}
		}
	}

	if !plan.RAM.Equal(state.RAM) {
		resizeReq.RAM = uint64(plan.RAM.ValueInt64())
	}

	tflog.Info(ctx, fmt.Sprintf("ComputeResourceResize: changing CPU %d -> %d and/or RAM %d -> %d\"",
		state.CPU.ValueInt64(), plan.CPU.ValueInt64(), state.RAM.ValueInt64(), plan.RAM.ValueInt64()))
	tflog.Info(ctx, "ComputeResourceResize: before calling CloudAPI().Compute().Resize", map[string]any{"compute_id": computeId, "req": resizeReq})
	res, err := c.CloudAPI().Compute().Resize(ctx, resizeReq)
	tflog.Info(ctx, "ComputeResourceResize: response from CloudAPI().Compute().Resize", map[string]any{"compute_id": computeId, "response": res})
	if err != nil {
		diags.AddError(
			"ComputeResourceResize: Unable to resize Compute",
			err.Error(),
		)
		return diags
	}

	tflog.Info(ctx, "ComputeResourceResize: compute is successfully resize", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceBootDiskResize(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceBootDiskResize: start resize boot disk size", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceBootDiskResize: cannot parsed ID compute from state", err.Error())
		return diags
	}

	if state.BootDiskSize.ValueInt64() > plan.BootDiskSize.ValueInt64() {
		diags.AddError(fmt.Sprintf("ComputeResourceBootDiskResize: compute ID %d - shrinking boot disk is not allowed, state: %d, plan: %d", computeId, state.BootDiskSize.ValueInt64(), plan.BootDiskSize.ValueInt64()), "")
		return diags
	}

	req := compute.DiskResizeRequest{
		ComputeID: computeId,
		Size:      uint64(plan.BootDiskSize.ValueInt64()),
	}

	if !state.BootDiskId.IsNull() {
		req.DiskID = uint64(state.BootDiskId.ValueInt64())
	} else {
		bootDisk, diags := ComputeResourceBootDiskCheckPresence(ctx, state, c)
		if diags.HasError() {
			return diags
		}
		req.DiskID = bootDisk.ID
	}

	tflog.Info(ctx, fmt.Sprintf("ComputeResourceBootDiskResize: compute ID %d, boot disk ID %d resize %d -> %d",
		computeId, req.DiskID, state.BootDiskSize.ValueInt64(), req.Size))
	tflog.Info(ctx, "ComputeResourceBootDiskResize: before calling CloudAPI().Compute().DiskResize", map[string]any{"compute_id": computeId, "req": req})
	res, err := c.CloudAPI().Compute().DiskResize(ctx, req)
	tflog.Info(ctx, "ComputeResourceBootDiskResize: response from CloudAPI().Compute().DiskResize", map[string]any{"compute_id": computeId, "response": res})
	if err != nil {
		diags.AddError(
			"ComputeResourceBootDiskResize: Unable to resize boot disk",
			err.Error(),
		)
		return diags
	}

	tflog.Info(ctx, "ComputeResourceBootDiskResize: boot disk is successfully resize", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceExtraDiskUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceExtraDiskUpdate: start update extra disk(s) list to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceExtraDiskUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	attachSet, detachSet := differenceSimpleType(state.ExtraDisks, plan.ExtraDisks)

	for _, disk := range attachSet {
		diskId := uint64(disk.(types.Int64).ValueInt64())
		tflog.Info(ctx, fmt.Sprintf("ComputeResourceExtraDiskUpdate: Start attach extra disk with ID - %d to compute with ID - %d", diskId, computeId))
		req := compute.DiskAttachRequest{
			ComputeID: computeId,
			DiskID:    diskId,
		}
		res, err := c.CloudAPI().Compute().DiskAttach(ctx, req)
		tflog.Info(ctx, "ComputeResourceExtraDiskUpdate: response from CloudAPI().Compute().DiskAttach", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceExtraDiskUpdate: Cannot attach disk with ID - %d", diskId), err.Error())
		}
	}

	if diags.HasError() {
		return diags
	}

	for _, disk := range detachSet {
		diskId := uint64(disk.(types.Int64).ValueInt64())
		tflog.Info(ctx, fmt.Sprintf("ComputeResourceExtraDiskUpdate: Start detach extra disk with ID - %d from compute with ID - %d", diskId, computeId))
		req := compute.DiskDetachRequest{
			ComputeID: computeId,
			DiskID:    diskId,
		}
		res, err := c.CloudAPI().Compute().DiskDetach(ctx, req)
		tflog.Info(ctx, "ComputeResourceExtraDiskUpdate: response from CloudAPI().Compute().DiskDetach", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceExtraDiskUpdate: Cannot detach disk with ID - %d", diskId), err.Error())
		}
	}

	if diags.HasError() {
		tflog.Error(ctx, "ComputeResourceExtraDiskUpdate: Errors occurred while managing disks")
		return diags
	}

	tflog.Info(ctx, "ComputeResourceExtraDiskUpdate: extra disks is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourcePCIDeviceUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourcePCIDeviceUpdate: start update PCI device(s) list to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourcePCIDeviceUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	attachSet, detachSet := differenceSimpleType(state.PCIDevices, plan.PCIDevices)

	for _, pciDevice := range detachSet {
		pciDeviceId := pciDevice.(types.Int64).ValueInt64()
		tflog.Info(ctx, fmt.Sprintf("ComputeResourcePCIDeviceUpdate: Start detach PCI device with ID - %d from compute with ID - %d", pciDeviceId, computeId))
		req := compute.DetachPCIDeviceRequest{
			ComputeID: computeId,
			DeviceID:  uint64(pciDeviceId),
		}
		res, err := c.CloudAPI().Compute().DetachPCIDevice(ctx, req)
		tflog.Info(ctx, "ComputeResourcePCIDeviceUpdate: response from CloudAPI().Compute().DetachPCIDevice", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourcePCIDeviceUpdate: Cannot detach PCI device with ID - %d", pciDeviceId), err.Error())
		}
	}

	for _, pciDevice := range attachSet {
		pciDeviceId := pciDevice.(types.Int64).ValueInt64()
		tflog.Info(ctx, fmt.Sprintf("ComputeResourcePCIDeviceUpdate: Start attach PCI device with ID - %d to compute with ID - %d", pciDeviceId, computeId))
		req := compute.AttachPCIDeviceRequest{
			ComputeID: computeId,
			DeviceID:  uint64(pciDeviceId),
		}
		res, err := c.CloudAPI().Compute().AttachPCIDevice(ctx, req)
		tflog.Info(ctx, "ComputeResourcePCIDeviceUpdate: response from CloudAPI().Compute().AttachPCIDevice", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourcePCIDeviceUpdate: Cannot attach PCI device with ID - %d", pciDeviceId), err.Error())
		}
	}

	if diags.HasError() {
		tflog.Error(ctx, "ComputeResourcePCIDeviceUpdate: Errors occurred while managing PCI device(s)")
		return diags
	}

	tflog.Info(ctx, "ComputeResourcePCIDeviceUpdate: PCI device(s) is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceNetworkUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceNetworkUpdate: start update network rules to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceNetworkUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	attachMap, changeIpMap, changeMacMap, detachMap := differenceNetwork(state.Network, plan.Network)

	tflog.Info(ctx, "ComputeResourceNetworkUpdate: start detach network(s) rules to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	for _, network := range detachMap {
		req := compute.NetDetachRequest{
			ComputeID: computeId,
			IPAddr:    network["ip_address"].(types.String).ValueString(),
			MAC:       network["mac"].(types.String).ValueString(),
		}
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: before calling CloudAPI().Compute().NetDetach", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().NetDetach(ctx, req)
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: response from CloudAPI().Compute().NetDetach", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceNetworkUpdate: failed to detach net ID %d from Compute ID %d",
				network["net_id"].(types.Int64).ValueInt64(), computeId), err.Error())
		}
	}

	if diags.HasError() {
		return diags
	}

	tflog.Info(ctx, "ComputeResourceNetworkUpdate: start change IP network(s) rules to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})

	for _, network := range changeIpMap {
		req := compute.ChangeIPRequest{
			ComputeID: computeId,
			NetType:   network["net_type"].(types.String).ValueString(),
			NetID:     uint64(network["net_id"].(types.Int64).ValueInt64()),
			IPAddr:    network["ip_address"].(types.String).ValueString(),
		}
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: before calling CloudAPI().Compute().ChangeIP", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().ChangeIP(ctx, req)
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: response from CloudAPI().Compute().ChangeIP", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceNetworkUpdate: failed to change IP net ID %d from Compute ID %d",
				network["net_id"].(types.Int64).ValueInt64(), computeId), err.Error())
		}
	}

	if diags.HasError() {
		return diags
	}

	needStart := false
	// need stop to attach first network
	if len(detachMap) == len(state.Network.Elements()) || (len(state.Network.Elements()) < 1) && len(attachMap) > 0 || hasDPDKnetwork(attachMap) || len(changeMacMap) > 0 {
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: stop compute", map[string]any{"compute_id": computeId})
		_, err = c.CloudAPI().Compute().Stop(ctx, compute.StopRequest{ComputeID: computeId})
		if err != nil {
			diags.AddError(
				"ComputeResourceNetworkUpdate: cannot stop compute", err.Error(),
			)
			return diags
		}
		if plan.Started.ValueBool() {
			needStart = true
		}
	}

	tflog.Info(ctx, "ComputeResourceNetworkUpdate: start change MAC address network(s) to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})

	for _, network := range changeMacMap {
		req := compute.ChangeMACRequest{
			ComputeID:  computeId,
			NewMAC:     network["mac"].(types.String).ValueString(),
			СurrentMAC: network["old_mac"].(types.String).ValueString(),
		}
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: before calling CloudAPI().Compute().ChangeMAC", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().ChangeMAC(ctx, req)
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: response from CloudAPI().Compute().ChangeMAC", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceNetworkUpdate: failed to change Mac to net ID %d from Compute ID %d",
				network["net_id"].(types.Int64).ValueInt64(), computeId), err.Error())
		}
	}

	if diags.HasError() {
		return diags
	}

	sort.Slice(attachMap, func(i, j int) bool {
		weightI := attachMap[i]["weight"].(types.Int64).ValueInt64()
		weightJ := attachMap[j]["weight"].(types.Int64).ValueInt64()
		if weightI == 0 {
			return false
		}
		if weightJ == 0 {
			return true
		}
		return weightI < weightJ
	})

	for _, network := range attachMap {
		req := compute.NetAttachRequest{
			ComputeID: computeId,
			NetType:   strings.ToUpper(network["net_type"].(types.String).ValueString()),
			NetID:     uint64(network["net_id"].(types.Int64).ValueInt64()),
		}
		if req.NetType == "DPDK" {
			req.MTU = uint64(network["mtu"].(types.Int64).ValueInt64())
		}
		ipaddr, ipSet := network["ip_address"]
		if ipSet {
			req.IPAddr = ipaddr.(types.String).ValueString()
		}
		macaddr, macSet := network["mac"]
		if macSet {
			req.MACAddr = macaddr.(types.String).ValueString()
		}
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: before calling CloudAPI().Compute().NetAttach", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().NetAttach(ctx, req)
		tflog.Info(ctx, "ComputeResourceNetworkUpdate: response from CloudAPI().Compute().NetAttach", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceNetworkUpdate: failed to attach net ID %d from Compute ID %d",
				network["net_id"].(types.Int64).ValueInt64(), computeId), err.Error())
		}
	}

	if diags.HasError() {
		return diags
	}

	if needStart {
		diags = ComputeResourceStartStop(ctx, plan, c)
	}

	if diags.HasError() {
		tflog.Error(ctx, "ComputeResourceExtraDiskUpdate: Errors occurred while managing network(s)")
		return diags
	}

	tflog.Info(ctx, "ComputeResourceExtraDiskUpdate: network(s) is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func hasDPDKnetwork(networkAttachMap []map[string]attr.Value) bool {
	for _, elem := range networkAttachMap {
		if elem["net_type"].(types.String).ValueString() == "DPDK" {
			return true
		}
	}
	return false
}

func ComputeResourceComputeUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceComputeUpdate: start update compute parameters", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceComputeUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	req := compute.UpdateRequest{
		ComputeID: computeId,
	}

	if !plan.Name.Equal(state.Name) {
		req.Name = plan.Name.ValueString()
	}

	if !plan.Description.IsUnknown() && !plan.Description.Equal(state.Description) {
		req.Description = plan.Description.ValueString()
	}

	if !plan.NumaAffinity.Equal(state.NumaAffinity) && !plan.NumaAffinity.IsUnknown() {
		req.NumaAffinity = plan.NumaAffinity.ValueString()
	}

	if !plan.CPUPin.Equal(state.CPUPin) && !plan.CPUPin.IsUnknown() {
		req.CPUPin = plan.CPUPin.ValueBool()
	}
	if !plan.HPBacked.Equal(state.HPBacked) && !plan.HPBacked.IsUnknown() {
		req.HPBacked = plan.HPBacked.ValueBool()
	}
	if !plan.LoaderType.Equal(state.LoaderType) && !plan.LoaderType.IsUnknown() {
		req.LoaderType = plan.LoaderType.ValueString()
	}

	if !plan.BootType.Equal(state.BootType) && !plan.BootType.IsUnknown() {
		req.BootType = plan.BootType.ValueString()
	}
	if !plan.HotResize.Equal(state.HotResize) && !plan.HotResize.IsUnknown() {
		req.HotResize = plan.HotResize.ValueBool()
	}
	if !plan.NetworkInterfaceNaming.Equal(state.NetworkInterfaceNaming) && !plan.NetworkInterfaceNaming.IsUnknown() {
		req.NetworkInterfaceNaming = plan.NetworkInterfaceNaming.ValueString()
	}

	if !plan.Chipset.IsUnknown() && !plan.Chipset.Equal(state.Chipset) {
		req.Chipset = plan.Chipset.ValueString()
	}

	if !plan.PreferredCPU.IsUnknown() {
		preferredCPUList := plan.PreferredCPU.Elements()
		preferredCPU := make([]int64, 0, len(preferredCPUList))
		for _, elem := range preferredCPUList {
			preferredCPU = append(preferredCPU, elem.(types.Int64).ValueInt64())
		}
		req.PreferredCPU = preferredCPU
	}
	if len(state.PreferredCPU.Elements()) != 0 && len(req.PreferredCPU) == 0 {
		req.PreferredCPU = []int64{-1}
	}

	if !plan.LoaderType.IsUnknown() && !plan.LoaderType.Equal(state.Description) {
		req.Description = plan.Description.ValueString()
	}

	// Note bene: numa_affinity, cpu_pin and hp_backed are not allowed to be changed for compute in STARTED tech status.
	// If STARTED, we need to stop it before update

	var isStopRequred bool
	if !plan.NumaAffinity.Equal(state.NumaAffinity) || !plan.CPUPin.Equal(state.CPUPin) || !plan.HPBacked.Equal(state.HPBacked) || !plan.Chipset.Equal(state.Chipset) || (len(req.PreferredCPU) != 0) {
		isStopRequred = true
		tflog.Info(ctx, "ComputeResourceComputeUpdate: stop compute", map[string]any{"compute_id": computeId})
		_, err = c.CloudAPI().Compute().Stop(ctx, compute.StopRequest{ComputeID: computeId})
		if err != nil {
			diags.AddError(
				"ComputeResourceComputeUpdate: cannot stop compute", err.Error(),
			)
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourceComputeUpdate: before calling CloudAPI().Compute().Update", map[string]any{"compute_id": computeId, "req": req})
	res, err := c.CloudAPI().Compute().Update(ctx, req)
	tflog.Info(ctx, "ComputeResourceComputeUpdate: response from CloudAPI().Compute().Update", map[string]any{"compute_id": computeId, "response": res})
	if err != nil {
		diags.AddError(
			"ComputeResourceComputeUpdate: Unable to update compute parameters",
			err.Error(),
		)
		return diags
	}

	if isStopRequred && plan.Started.ValueBool() {
		diags = ComputeResourceStartStop(ctx, plan, c)
	}

	if diags.HasError() {
		tflog.Error(ctx, "ComputeResourceExtraDiskUpdate: Errors occurred while managing network(s)")
		return diags
	}

	tflog.Info(ctx, "ComputeResourceComputeUpdate: compute parameters is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceAffinityLabelUpdate(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceAffinityLabelUpdate: start resize boot disk size", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceAffinityLabelUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	if plan.AffinityLabel.ValueString() == "" {
		req := compute.AffinityLabelRemoveRequest{
			ComputeID: computeId,
		}
		tflog.Info(ctx, "ComputeResourceAffinityLabelUpdate: before calling CloudAPI().Compute().AffinityLabelRemove", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().AffinityLabelRemove(ctx, req)
		tflog.Info(ctx, "ComputeResourceAffinityLabelUpdate: response from CloudAPI().Compute().AffinityLabelRemove", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceAffinityLabelUpdate: Unable to remove affinity label",
				err.Error(),
			)
			return diags
		}
	} else {
		req := compute.AffinityLabelSetRequest{
			ComputeID:     computeId,
			AffinityLabel: plan.AffinityLabel.ValueString(),
		}
		tflog.Info(ctx, "ComputeResourceAffinityLabelUpdate: before calling CloudAPI().Compute().AffinityLabelSet", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().AffinityLabelSet(ctx, req)
		tflog.Info(ctx, "ComputeResourceAffinityLabelUpdate: response from CloudAPI().Compute().AffinityLabelSet", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceAffinityLabelUpdate: Unable to set affinity label",
				err.Error(),
			)
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourceAffinityLabelUpdate: affinity label is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceAffinityRulesUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceAffinityRulesUpdate: start update affinity rules to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceAffinityRulesUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	if len(plan.AffinityRules.Elements()) == 0 {
		req := compute.AffinityRulesClearRequest{
			ComputeID: computeId,
		}
		tflog.Info(ctx, "ComputeResourceAffinityRulesUpdate: before calling CloudAPI().Compute().AffinityRulesClear", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().AffinityRulesClear(ctx, req)
		tflog.Info(ctx, "ComputeResourceAffinityRulesUpdate: response from CloudAPI().Compute().AffinityRulesClear", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceAffinityRulesUpdate: failed to clear affinity rules from Compute ID %d",
				computeId), err.Error())
		}
		return diags
	} else {
		attachSet, detachSet := differenceAffinityRules(state.AffinityRules, plan.AffinityRules)

		for _, affinityRules := range detachSet {
			var itemAffinityRules models.ItemResourceRulesModel
			objVal := affinityRules.(attr.Value)
			diags = objVal.(types.Object).As(ctx, &itemAffinityRules, basetypes.ObjectAsOptions{})
			if diags.HasError() {
				tflog.Error(ctx, "ComputeResourceAffinityRulesUpdate Error converting object to struct")
				diags.AddError(
					fmt.Sprintf("ComputeResourceAffinityRulesUpdate: Unable to set affinity rules for compute %d", computeId),
					"cannot populate itemsAffinityRules with plan.AffinityRules List elements")
				return diags
			}
			req := compute.AffinityRuleRemoveRequest{
				ComputeID: computeId,
				Topology:  strings.ToLower(itemAffinityRules.Topology.ValueString()),
				Policy:    strings.ToUpper(itemAffinityRules.Policy.ValueString()),
				Mode:      strings.ToUpper(itemAffinityRules.Mode.ValueString()),
				Key:       itemAffinityRules.Key.ValueString(),
				Value:     itemAffinityRules.Value.ValueString(),
			}

			tflog.Info(ctx, "ComputeAffinityRules: before calling CloudAPI().Compute().AffinityRuleRemove", map[string]any{"compute_id": computeId, "req": req})
			res, err := c.CloudAPI().Compute().AffinityRuleRemove(ctx, req)
			tflog.Info(ctx, "ComputeAffinityRules: response from CloudAPI().Compute().AffinityRuleRemove", map[string]any{"compute_id": computeId, "response": res})
			if err != nil {
				diags.AddError(
					"ComputeAffinityRules: Unable to AffinityRuleRemove for Compute",
					err.Error(),
				)
			}
		}

		if diags.HasError() {
			return diags
		}

		for _, affinityRules := range attachSet {
			var itemsAffinityRules models.ItemResourceRulesModel
			objVal := affinityRules.(attr.Value)
			diags = objVal.(types.Object).As(ctx, &itemsAffinityRules, basetypes.ObjectAsOptions{})
			if diags.HasError() {
				tflog.Error(ctx, "ComputeResourceAffinityRulesUpdate Error converting object to struct")
				diags.AddError(
					fmt.Sprintf("ComputeResourceAffinityRulesUpdate: Unable to set affinity rules for compute %d", computeId),
					"cannot populate itemsAffinityRules with plan.AffinityRules List elements")
				return diags
			}
			req := compute.AffinityRuleAddRequest{
				ComputeID: computeId,
				Topology:  strings.ToLower(itemsAffinityRules.Topology.ValueString()),
				Policy:    strings.ToUpper(itemsAffinityRules.Policy.ValueString()),
				Mode:      strings.ToUpper(itemsAffinityRules.Mode.ValueString()),
				Key:       itemsAffinityRules.Key.ValueString(),
				Value:     itemsAffinityRules.Value.ValueString(),
			}

			tflog.Info(ctx, "ComputeAffinityRules: before calling CloudAPI().Compute().AffinityRuleAdd", map[string]any{"compute_id": computeId, "req": req})
			res, err := c.CloudAPI().Compute().AffinityRuleAdd(ctx, req)
			tflog.Info(ctx, "ComputeAffinityRules: response from CloudAPI().Compute().AffinityRuleAdd", map[string]any{"compute_id": computeId, "response": res})
			if err != nil {
				diags.AddError(
					"ComputeAffinityRules: Unable to AffinityRuleAdd for Compute",
					err.Error(),
				)
			}
		}

		if diags.HasError() {
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourceAffinityRulesUpdate: affinity rules is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceAntiAffinityRulesUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceAntiAffinityRulesUpdate: start update affinity rules to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceAntiAffinityRulesUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	if len(plan.AntiAffinityRules.Elements()) == 0 {
		req := compute.AntiAffinityRulesClearRequest{
			ComputeID: computeId,
		}
		tflog.Info(ctx, "ComputeResourceAntiAffinityRulesUpdate: before calling CloudAPI().Compute().AntiAffinityRulesClear", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().AntiAffinityRulesClear(ctx, req)
		tflog.Info(ctx, "ComputeResourceAntiAffinityRulesUpdate: response from CloudAPI().Compute().AntiAffinityRulesClear", map[string]any{"compute_id": plan.ID.ValueString(), "response": res})
		if err != nil {
			diags.AddError(fmt.Sprintf("ComputeResourceAntiAffinityRulesUpdate: failed to clear affinity rules from Compute ID %d",
				computeId), err.Error())
		}
		return diags
	} else {
		attachSet, detachSet := differenceAffinityRules(state.AntiAffinityRules, plan.AntiAffinityRules)

		for _, antiAffinityRules := range detachSet {
			var itemAntiAffinityRules models.ItemResourceRulesModel
			objVal := antiAffinityRules.(attr.Value)
			diags = objVal.(types.Object).As(ctx, &itemAntiAffinityRules, basetypes.ObjectAsOptions{})
			if diags.HasError() {
				tflog.Error(ctx, "ComputeResourceAntiAffinityRulesUpdate Error converting object to struct")
				diags.AddError(
					fmt.Sprintf("ComputeResourceAntiAffinityRulesUpdate: Unable to set anti affinity rules for compute %d", computeId),
					"cannot populate itemsAntiAntiAffinityRules with plan.AntiAntiAffinityRules List elements")
				return diags
			}
			req := compute.AntiAffinityRuleRemoveRequest{
				ComputeID: computeId,
				Topology:  strings.ToLower(itemAntiAffinityRules.Topology.ValueString()),
				Policy:    strings.ToUpper(itemAntiAffinityRules.Policy.ValueString()),
				Mode:      strings.ToUpper(itemAntiAffinityRules.Mode.ValueString()),
				Key:       itemAntiAffinityRules.Key.ValueString(),
				Value:     itemAntiAffinityRules.Value.ValueString(),
			}

			tflog.Info(ctx, "ComputeAntiAffinityRules: before calling CloudAPI().Compute().AntiAffinityRuleRemove", map[string]any{"compute_id": computeId, "req": req})
			res, err := c.CloudAPI().Compute().AntiAffinityRuleRemove(ctx, req)
			tflog.Info(ctx, "ComputeAntiAffinityRules: response from CloudAPI().Compute().AntiAffinityRuleRemove", map[string]any{"compute_id": computeId, "response": res})
			if err != nil {
				diags.AddError(
					"ComputeAntiAffinityRules: Unable to AntiAffinityRuleRemove for Compute",
					err.Error(),
				)
			}
		}

		if diags.HasError() {
			return diags
		}

		for _, antiAffinityRules := range attachSet {
			var itemsAntiAffinityRules models.ItemResourceRulesModel
			objVal := antiAffinityRules.(attr.Value)
			diags = objVal.(types.Object).As(ctx, &itemsAntiAffinityRules, basetypes.ObjectAsOptions{})
			if diags.HasError() {
				tflog.Error(ctx, "ComputeResourceAntiAffinityRulesUpdate Error converting object to struct")
				diags.AddError(
					fmt.Sprintf("ComputeResourceAntiAffinityRulesUpdate: Unable to set anti affinity rules for compute %d", computeId),
					"cannot populate itemsAntiAffinityRules with plan.AntiAffinityRules List elements")
				return diags
			}
			req := compute.AntiAffinityRuleAddRequest{
				ComputeID: computeId,
				Topology:  strings.ToLower(itemsAntiAffinityRules.Topology.ValueString()),
				Policy:    strings.ToUpper(itemsAntiAffinityRules.Policy.ValueString()),
				Mode:      strings.ToUpper(itemsAntiAffinityRules.Mode.ValueString()),
				Key:       itemsAntiAffinityRules.Key.ValueString(),
				Value:     itemsAntiAffinityRules.Value.ValueString(),
			}

			tflog.Info(ctx, "ComputeAntiAffinityRules: before calling CloudAPI().Compute().AntiAffinityRuleAdd", map[string]any{"compute_id": computeId, "req": req})
			res, err := c.CloudAPI().Compute().AntiAffinityRuleAdd(ctx, req)
			tflog.Info(ctx, "ComputeAntiAffinityRules: response from CloudAPI().Compute().AntiAffinityRuleAdd", map[string]any{"compute_id": computeId, "response": res})
			if err != nil {
				diags.AddError(
					"ComputeAntiAffinityRules: Unable to AntiAffinityRuleAdd for Compute",
					err.Error(),
				)
			}
		}

		if diags.HasError() {
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourceAntiAffinityRulesUpdate: affinity rules is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceTagsUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceTagsUpdate: start update tags to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceTagsUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	addedTags, deleteTags := differenceTags(state.Tags, plan.Tags)

	for _, tags := range deleteTags {
		req := compute.TagRemoveRequest{ComputeID: computeId}
		objTag := tags.(types.Object)
		tagMap := objTag.Attributes()
		req.Key = tagMap["key"].(types.String).ValueString()
		tflog.Info(ctx, "ComputeResourceTags: before calling CloudAPI().Compute().TagRemove", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().TagRemove(ctx, req)
		tflog.Info(ctx, "ComputeResourceTags: response from CloudAPI().Compute().TagRemove", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceTags: Unable to remove tags for Compute",
				err.Error(),
			)
		}
	}

	if diags.HasError() {
		return diags
	}

	for _, tags := range addedTags {
		req := compute.TagAddRequest{ComputeID: computeId}
		objTag := tags.(types.Object)
		tagMap := objTag.Attributes()
		req.Key = tagMap["key"].(types.String).ValueString()
		req.Value = tagMap["value"].(types.String).ValueString()
		tflog.Info(ctx, "ComputeResourceTags: before calling CloudAPI().Compute().TagAdd", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().TagAdd(ctx, req)
		tflog.Info(ctx, "ComputeResourceTags: response from CloudAPI().Compute().TagAdd", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddWarning(
				"ComputeResourceTags: Unable to add tags for Compute",
				err.Error(),
			)
		}
	}

	if diags.HasError() {
		return diags
	}

	tflog.Info(ctx, "ComputeResourceTagsUpdate: tags is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourcePortForwardingUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourcePortForwardingUpdate: start update port forwarding rules to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourcePortForwardingUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	addSet, delSet := differencePortForwarding(state.PortForwarding, plan.PortForwarding)

	for _, pfw := range delSet {
		var itemPortForwarding models.ItemPortForfardingModel
		objVal := pfw.(attr.Value)
		diags = objVal.(types.Object).As(ctx, &itemPortForwarding, basetypes.ObjectAsOptions{})
		if diags.HasError() {
			tflog.Error(ctx, "ComputeResourcePortForwardingUpdate Error converting object to struct")
			diags.AddError(
				fmt.Sprintf("ComputeResourcePortForwardingUpdate: Unable to set port forwarding rules for compute %d", computeId),
				"cannot populate itemPortForwarding with plan.PortForwarding List elements")
			return diags
		}
		req := compute.PFWDelRequest{
			ComputeID:       computeId,
			PublicPortStart: uint64(itemPortForwarding.PublicPortStart.ValueInt64()),
			Proto:           strings.ToLower(itemPortForwarding.Proto.ValueString()),
		}
		if itemPortForwarding.PublicPortStart.ValueInt64() != 0 {
			req.PublicPortStart = uint64(itemPortForwarding.PublicPortStart.ValueInt64())
		}
		if itemPortForwarding.LocalPort.ValueInt64() != 0 {
			req.LocalBasePort = uint64(itemPortForwarding.LocalPort.ValueInt64())
		}

		tflog.Info(ctx, "ComputeResourcePortForwardingUpdate: before calling CloudAPI().Compute().PFWDel", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().PFWDel(ctx, req)
		tflog.Info(ctx, "ComputeResourcePortForwardingUpdate: response from CloudAPI().Compute().PFWDel", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourcePortForwardingUpdate: Unable to port forwarding rules delete for Compute",
				err.Error(),
			)
		}
	}

	if diags.HasError() {
		return diags
	}

	for _, pfw := range addSet {
		var itemPortForwarding models.ItemPortForfardingModel
		objVal := pfw.(attr.Value)
		diags = objVal.(types.Object).As(ctx, &itemPortForwarding, basetypes.ObjectAsOptions{})
		if diags.HasError() {
			tflog.Error(ctx, "ComputeResourcePortForwardingUpdate Error converting object to struct")
			diags.AddError(
				fmt.Sprintf("ComputeResourcePortForwardingUpdate: Unable to set port forwarding rules for compute %d", computeId),
				"cannot populate itemPortForwarding with plan.PortForwarding List elements")
			return diags
		}
		req := compute.PFWAddRequest{
			ComputeID:       computeId,
			PublicPortStart: uint64(itemPortForwarding.PublicPortStart.ValueInt64()),
			Proto:           strings.ToLower(itemPortForwarding.Proto.ValueString()),
		}
		if itemPortForwarding.PublicPortStart.ValueInt64() != 0 {
			req.PublicPortStart = uint64(itemPortForwarding.PublicPortStart.ValueInt64())
		}
		if itemPortForwarding.LocalPort.ValueInt64() != 0 {
			req.LocalBasePort = uint64(itemPortForwarding.LocalPort.ValueInt64())
		}

		tflog.Info(ctx, "ComputeResourcePortForwardingUpdate: before calling CloudAPI().Compute().PFWAdd", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().PFWAdd(ctx, req)
		tflog.Info(ctx, "ComputeResourcePortForwardingUpdate: response from CloudAPI().Compute().PFWAdd", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourcePortForwardingUpdate: Unable to port forwarding rules add for Compute",
				err.Error(),
			)
		}
	}

	if diags.HasError() {
		return diags
	}

	tflog.Info(ctx, "ComputeResourcePortForwardingUpdate: port forwarding rules is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceUserAccessUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceUserAccessUpdate: start update user access rules to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceUserAccessUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	addedUserAccess, deleteUserAccess := differenceUserAccess(state.UserAccess, plan.UserAccess)

	for _, userAccess := range deleteUserAccess {
		req := compute.UserRevokeRequest{ComputeID: computeId}
		objTag := userAccess.(types.Object)
		tagMap := objTag.Attributes()
		req.Username = tagMap["username"].(types.String).ValueString()
		tflog.Info(ctx, "ComputeResourceUserAccessUpdate: before calling CloudAPI().Compute().UserRevoke", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().UserRevoke(ctx, req)
		tflog.Info(ctx, "ComputeResourceUserAccessUpdate: response from CloudAPI().Compute().UserRevoke", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceUserAccessUpdate: Unable to revoke user access rules for Compute",
				err.Error(),
			)
		}
	}

	if diags.HasError() {
		return diags
	}

	for _, userAccess := range addedUserAccess {
		req := compute.UserGrantRequest{ComputeID: computeId}
		objTag := userAccess.(types.Object)
		tagMap := objTag.Attributes()
		req.Username = tagMap["username"].(types.String).ValueString()
		req.AccessType = tagMap["access_type"].(types.String).ValueString()
		tflog.Info(ctx, "ComputeResourceUserAccessUpdate: before calling CloudAPI().Compute().UserGrant", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().UserGrant(ctx, req)
		tflog.Info(ctx, "ComputeResourceUserAccessUpdate: response from CloudAPI().Compute().UserGrant", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceUserAccessUpdate: Unable to add user access rules for Compute",
				err.Error(),
			)
		}
	}

	if diags.HasError() {
		return diags
	}

	tflog.Info(ctx, "ComputeResourceUserAccessUpdate: user access rules is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceSnapshotUpdate(ctx context.Context, state *models.ResourceComputeModel, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceSnapshotUpdate: start update snapshot(s) to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceSnapshotUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	addedLabel, deleteLabel := differenceSnapshot(state.Snapshot, plan.Snapshot)

	for _, label := range deleteLabel {
		objTag := label.(types.Object)
		tagMap := objTag.Attributes()
		reqLabel := tagMap["label"].(types.String).ValueString()
		req := compute.SnapshotDeleteRequest{ComputeID: computeId, Label: reqLabel}
		if (state.SnapshotDeleteAsync).ValueBool() {
			tflog.Info(ctx, "ComputeResourceSnapshotUpdate: before calling CloudAPI().Compute().SnapshotDeleteAsync", map[string]any{"compute_id": computeId, "req": req})
			res, err := c.CloudAPI().Compute().SnapshotDeleteAsync(ctx, req)
			req.Label = reqLabel
			tflog.Info(ctx, "ComputeResourceSnapshotUpdate: response from CloudAPI().Compute().SnapshotDeleteAsync", map[string]any{"compute_id": computeId, "response": res})
			if err != nil {
				diags.AddError(
					"ComputeResourceSnapshotUpdate: Unable to async delete snapshot for Compute",
					err.Error(),
				)
			}
		} else {

			tflog.Info(ctx, "ComputeResourceSnapshotUpdate: before calling CloudAPI().Compute().SnapshotDelete", map[string]any{"compute_id": computeId, "req": req})
			res, err := c.CloudAPI().Compute().SnapshotDelete(ctx, req)
			req.Label = reqLabel
			tflog.Info(ctx, "ComputeResourceSnapshotUpdate: response from CloudAPI().Compute().SnapshotDelete", map[string]any{"compute_id": computeId, "response": res})
			if err != nil {
				diags.AddError(
					"ComputeResourceSnapshotUpdate: Unable to delete snapshot for Compute",
					err.Error(),
				)
			}
		}

	}

	if diags.HasError() {
		return diags
	}

	for _, label := range addedLabel {
		req := compute.SnapshotCreateRequest{ComputeID: computeId}
		objTag := label.(types.Object)
		tagMap := objTag.Attributes()
		req.Label = tagMap["label"].(types.String).ValueString()
		tflog.Info(ctx, "ComputeResourceSnapshotUpdate: before calling CloudAPI().Compute().SnapshotCreate", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().SnapshotCreate(ctx, req)
		tflog.Info(ctx, "ComputeResourceSnapshotUpdate: response from CloudAPI().Compute().SnapshotCreate", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceSnapshotUpdate: Unable to create snapshot for Compute",
				err.Error(),
			)
		}
	}

	if diags.HasError() {
		return diags
	}

	tflog.Info(ctx, "ComputeResourceUserAccessUpdate: snapshot(s) is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceRollback(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceRollback: start rollback to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceRollback: cannot parsed ID compute from state", err.Error())
		return diags
	}

	label := plan.Rollback.Attributes()["label"].(types.String).ValueString()

	err = ic.ExistSnapshotInCompute(ctx, computeId, label, c)
	if err != nil {
		diags.AddError("ComputeResourceRollback: snapshot not found", err.Error())
		return diags
	}

	tflog.Info(ctx, "ComputeResourceRollback: stop compute", map[string]any{"compute_id": computeId})
	_, err = c.CloudAPI().Compute().Stop(ctx, compute.StopRequest{ComputeID: computeId})
	if err != nil {
		diags.AddError(
			"ComputeResourceRollback: cannot stop compute", err.Error(),
		)
		return diags
	}

	req := compute.SnapshotRollbackRequest{ComputeID: computeId}
	tagMap := plan.Rollback.Attributes()
	req.Label = tagMap["label"].(types.String).ValueString()
	tflog.Info(ctx, "ComputeResourceRollback: before calling CloudAPI().Compute().SnapshotRollback", map[string]any{"compute_id": computeId, "req": req})
	res, err := c.CloudAPI().Compute().SnapshotRollback(ctx, req)
	tflog.Info(ctx, "ComputeResourceRollback: response from CloudAPI().Compute().SnapshotRollback", map[string]any{"compute_id": computeId, "response": res})
	if err != nil {
		diags.AddError(
			"ComputeResourceRollback: Unable to rollback for Compute",
			err.Error(),
		)
		return diags
	}

	if plan.Started.ValueBool() {
		diags = ComputeResourceStartStop(ctx, plan, c)
		if diags.HasError() {
			tflog.Error(ctx, "ComputeResourceRollback: cannot start compute")
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourceRollback: rollback is successfully", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceCDUpdate(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceCDUpdate: start update cd list to compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceCDUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	if plan.CDImageID.IsNull() {
		tflog.Info(ctx, "ComputeResourceCDUpdate: before calling CloudAPI().Compute().CDEject", map[string]any{"compute_id": computeId})
		res, err := c.CloudAPI().Compute().CDEject(ctx, compute.CDEjectRequest{ComputeID: computeId})
		tflog.Info(ctx, "ComputeResourceCDUpdate: response from CloudAPI().Compute().CDEject", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceCDUpdate: Unable to eject cd from Compute",
				err.Error(),
			)
			return diags
		}
	} else {
		req := compute.CDInsertRequest{ComputeID: computeId}
		req.CDROMID = uint64(plan.CDImageID.ValueInt64())
		tflog.Info(ctx, "ComputeResourceCDUpdate: before calling CloudAPI().Compute().CDInsert", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().CDInsert(ctx, req)
		tflog.Info(ctx, "ComputeResourceCDUpdate: response from CloudAPI().Compute().CDInsert", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceCDUpdate: Unable to eject cd from Compute",
				err.Error(),
			)
		}
	}

	tflog.Info(ctx, "ComputeResourceUserAccessUpdate: cd list is successfully update", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourcePinToStackUpdate(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourcePinToStackUpdate: Start pin/unpin to stack compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourcePinToStackUpdate: cannot parsed ID compute from plan", err.Error())
		return diags
	}

	pinToStack := plan.PinToStack.ValueBool()

	tflog.Info(ctx, "ComputeResourcePinToStackUpdate: compute to be pin to stack", map[string]any{
		"compute_id":   computeId,
		"pin_to_stack": pinToStack})

	if pinToStack {
		req := compute.PinToStackRequest{
			ComputeID: computeId,
			AutoStart: plan.AutoStartWithNode.ValueBool(),
		}
		_, err = c.CloudAPI().Compute().PinToStack(ctx, req)
		tflog.Info(ctx, "ComputeResourcePinToStackUpdate: response from CloudAPI().Compute().PinToStack", map[string]any{"compute_id": computeId})
		if err != nil {
			diags.AddError(
				"ComputeResourcePinToStackUpdate: cannot pin to stack compute", err.Error(),
			)
			return diags
		}
	} else {
		if plan.AutoStartWithNode.ValueBool() {
			diags.AddError(
				"ComputeResourcePinToStackUpdate: for unpin compute the auto_start_w_node parameter must be false", "",
			)
			return diags
		}
		_, err = c.CloudAPI().Compute().UnpinFromStack(ctx, compute.UnpinFromStackRequest{ComputeID: computeId})
		tflog.Info(ctx, "ComputeResourcePinToStackUpdate: response from CloudAPI().Compute().UnpinFromStack", map[string]any{"compute_id": computeId})
		if err != nil {
			diags.AddError(
				"ComputeResourcePinToStackUpdate: cannot unpin from stack compute", err.Error(),
			)
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourcePinToStackUpdate: compute is successfully pin/unpin to stack", map[string]any{"compute_id": computeId, "pin_to_stack": pinToStack})
	return nil
}

func ComputeResourcePauseResumeCompute(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourcePauseResumeCompute: Start pause/resume compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourcePauseResumeCompute: cannot parsed ID compute from plan", err.Error())
		return diags
	}

	pause := plan.Pause.ValueBool()

	tflog.Info(ctx, "ComputeResourcePauseResumeCompute: compute to pause/resume", map[string]any{
		"compute_id": computeId,
		"pause":      pause})

	if pause {
		_, err = c.CloudAPI().Compute().Pause(ctx, compute.PauseRequest{ComputeID: computeId})
		tflog.Info(ctx, "ComputeResourcePauseResumeCompute: response from CloudAPI().Compute().Pause", map[string]any{"compute_id": computeId})
		if err != nil {
			diags.AddError(
				"ComputeResourcePauseResumeCompute: cannot pause compute", err.Error(),
			)
			return diags
		}
	} else {
		_, err = c.CloudAPI().Compute().Resume(ctx, compute.ResumeRequest{ComputeID: computeId})
		tflog.Info(ctx, "ComputeResourcePauseResumeCompute: response from CloudAPI().Compute().Resume", map[string]any{"compute_id": computeId})
		if err != nil {
			diags.AddError(
				"ComputeResourcePauseResumeCompute: cannot resume compute", err.Error(),
			)
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourcePauseResumeCompute: compute is successfully pause/resume", map[string]any{"compute_id": computeId, "pause": pause})
	return nil
}

func ComputeResourceResetCompute(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceResetCompute: Start reset compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceResetCompute: cannot parsed ID compute from plan", err.Error())
		return diags
	}

	tflog.Info(ctx, "ComputeResourceResetCompute: before calling CloudAPI().Compute().Reset", map[string]any{"compute_id": computeId})
	_, err = c.CloudAPI().Compute().Reset(ctx, compute.ResetRequest{ComputeID: computeId})
	tflog.Info(ctx, "ComputeResourcePauseResumeCompute: response from CloudAPI().Compute().Reset", map[string]any{"compute_id": computeId})
	if err != nil {
		diags.AddError(
			"ComputeResourcePauseResumeCompute: cannot reset compute", err.Error(),
		)
		return diags
	}

	tflog.Info(ctx, "ComputeResourcePauseResumeCompute: compute is successfully reset", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceRedeploy(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceRedeploy: Start redeploy compute with ID", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceRedeploy: cannot parsed ID compute from plan", err.Error())
		return diags
	}

	tflog.Info(ctx, "ComputeResourceRedeploy: stop compute", map[string]any{"compute_id": computeId})
	_, err = c.CloudAPI().Compute().Stop(ctx, compute.StopRequest{ComputeID: computeId})
	if err != nil {
		diags.AddError(
			"ComputeResourceRedeploy: cannot stop compute", err.Error(),
		)
		return diags
	}

	req := compute.RedeployRequest{
		ComputeID: computeId,
		ImageID:   uint64(plan.ImageID.ValueInt64()),
		DataDisks: "KEEP",
	}

	if !plan.BootDiskSize.IsNull() {
		req.DiskSize = uint64(plan.BootDiskSize.ValueInt64())
	}
	if !plan.Started.IsNull() {
		req.AutoStart = plan.Started.ValueBool()
	}
	if !plan.ForceStop.IsNull() {
		req.ForceStop = plan.ForceStop.ValueBool()
	}

	tflog.Info(ctx, "ComputeResourceRedeploy: before calling CloudAPI().Compute().Redeploy", map[string]any{"compute_id": computeId})
	_, err = c.CloudAPI().Compute().Redeploy(ctx, req)
	tflog.Info(ctx, "ComputeResourceRedeploy: response from CloudAPI().Compute().Redeploy", map[string]any{"compute_id": computeId})
	if err != nil {
		diags.AddError(
			"ComputeResourceRedeploy: cannot redeploy compute", err.Error(),
		)
		return diags
	}

	if plan.Started.ValueBool() {
		diags = ComputeResourceStartStop(ctx, plan, c)
		if diags.HasError() {
			tflog.Error(ctx, "ComputeResourceRedeploy: cannot start compute")
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourceRedeploy: compute is successfully redeploy", map[string]any{"compute_id": computeId})
	return nil
}

func ComputeResourceCustomFieldUpdate(ctx context.Context, plan *models.ResourceComputeModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "ComputeResourceCustomFieldUpdate: start update custom fields", map[string]any{"compute_id": plan.ID.ValueString()})
	diags := diag.Diagnostics{}

	computeId, err := strconv.ParseUint(plan.ID.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("ComputeResourceCustomFieldUpdate: cannot parsed ID compute from state", err.Error())
		return diags
	}

	if plan.CustomFields.ValueString() == "" {
		req := compute.DeleteCustomFieldsRequest{
			ComputeID: computeId,
		}
		tflog.Info(ctx, "ComputeResourceCustomFieldUpdate: before calling CloudAPI().Compute().DeleteCustomFields", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().DeleteCustomFields(ctx, req)
		tflog.Info(ctx, "ComputeResourceCustomFieldUpdate: response from CloudAPI().Compute().DeleteCustomFields", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceCustomFieldUpdate: Unable to remove custom fields",
				err.Error(),
			)
			return diags
		}
	} else {
		customFields := plan.CustomFields.ValueString()
		customFields = strings.ReplaceAll(customFields, "\\", "")
		customFields = strings.ReplaceAll(customFields, "\n", "")
		customFields = strings.ReplaceAll(customFields, "\t", "")
		customFields = strings.TrimSpace(customFields)
		req := compute.SetCustomFieldsRequest{
			ComputeID:    computeId,
			CustomFields: customFields,
		}
		tflog.Info(ctx, "ComputeResourceCustomFieldUpdate: before calling CloudAPI().Compute().SetCustomFields", map[string]any{"compute_id": computeId, "req": req})
		res, err := c.CloudAPI().Compute().SetCustomFields(ctx, req)
		tflog.Info(ctx, "ComputeResourceCustomFieldUpdate: response from CloudAPI().Compute().SetCustomFields", map[string]any{"compute_id": computeId, "response": res})
		if err != nil {
			diags.AddError(
				"ComputeResourceCustomFieldUpdate: Unable to set custom fields",
				err.Error(),
			)
			return diags
		}
	}

	tflog.Info(ctx, "ComputeResourceCustomFieldUpdate: custom fields is successfully update", map[string]any{"compute_id": computeId})
	return nil
}
