package utilities

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"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/rg"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/rg/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/status"
)

func CreateRequestResourceRG(ctx context.Context, plan *models.ResourceRGModel) (rg.CreateRequest, diag.Diagnostics) {
	tflog.Info(ctx, "Start CreateRequestResourceRG", map[string]any{
		"name":       plan.Name.ValueString(),
		"account_id": plan.AccountID.ValueInt64(),
		"gid":        plan.GID.ValueInt64(),
	})

	// set up required parameters in resource group create request
	createReq := rg.CreateRequest{
		Name:      plan.Name.ValueString(),
		AccountID: uint64(plan.AccountID.ValueInt64()),
		GID:       uint64(plan.GID.ValueInt64()),
	}

	// set up quota optional parameters
	if !plan.Quota.IsUnknown() {
		var quota models.QuotaModel
		diags := plan.Quota.As(ctx, &quota, basetypes.ObjectAsOptions{})
		if diags.HasError() {
			return createReq, diags
		}

		if quota.CPU.IsUnknown() {
			createReq.MaxCPUCapacity = -1 // default value -1
		} else {
			createReq.MaxCPUCapacity = quota.CPU.ValueInt64()
		}
		if quota.Disk.IsUnknown() {
			createReq.MaxVDiskCapacity = -1 // default value -1
		} else {
			createReq.MaxVDiskCapacity = quota.Disk.ValueInt64()
		}
		if quota.Ram.IsUnknown() {
			createReq.MaxMemoryCapacity = -1 // default value -1
		} else {
			createReq.MaxMemoryCapacity = quota.Ram.ValueInt64()
		}
		if quota.ExtTraffic.IsUnknown() {
			createReq.MaxNetworkPeerTransfer = -1 // default value -1
		} else {
			createReq.MaxNetworkPeerTransfer = quota.ExtTraffic.ValueInt64()
		}
		if quota.ExtIps.IsUnknown() {
			createReq.MaxNumPublicIP = -1 // default value -1
		} else {
			createReq.MaxNumPublicIP = quota.ExtIps.ValueInt64()
		}
	} else {
		createReq.MaxCPUCapacity = -1
		createReq.MaxVDiskCapacity = -1
		createReq.MaxMemoryCapacity = -1
		createReq.MaxNetworkPeerTransfer = -1
		createReq.MaxNumPublicIP = -1
	}

	// set up defNet, owner, ipcidr, description, extNetId, extIp, registerComputes optional parameters
	if !plan.DefNetType.IsNull() {
		createReq.DefNet = plan.DefNetType.ValueString()
	}
	if !plan.Owner.IsNull() {
		createReq.Owner = plan.Owner.ValueString()
	}
	if !plan.IPCIDR.IsNull() {
		createReq.IPCIDR = plan.IPCIDR.ValueString()
	}
	if !plan.Description.IsNull() {
		createReq.Description = plan.Description.ValueString()
	}
	if plan.ExtNetID.IsNull() {
		createReq.ExtNetID = 0 // default value 0
	} else {
		createReq.ExtNetID = uint64(plan.ExtNetID.ValueInt64())
	}
	if !plan.ExtIP.IsNull() {
		createReq.ExtIP = plan.ExtIP.ValueString()
	}

	return createReq, nil
}

func RGCheckPresence(ctx context.Context, rgId uint64, c *client.Client) (*rg.RecordResourceGroup, error) {
	tflog.Info(ctx, fmt.Sprintf("Get info about resource group with ID - %v", rgId))

	recordRG, err := c.CloudAPI().RG().Get(ctx, rg.GetRequest{RGID: rgId})
	if err != nil {
		return nil, fmt.Errorf("cannot get info about resource group with error: %w", err)
	}

	tflog.Info(ctx, "UtilityRGCheckPresence resourceRG: response from CloudAPI().RG().Get", map[string]any{"rg_id": rgId, "response": recordRG})

	return recordRG, err
}

// RestoreRG performs resource group Restore request. Returns error in case of failures.
func RestoreRG(ctx context.Context, rgId uint64, c *client.Client) diag.Diagnostics {
	diags := diag.Diagnostics{}

	restoreReq := rg.RestoreRequest{RGID: rgId}

	tflog.Info(ctx, "utilityRestoreRG: before calling CloudAPI().RG().Restore", map[string]any{"rgId": rgId, "req": restoreReq})

	res, err := c.CloudAPI().RG().Restore(ctx, restoreReq)
	if err != nil {
		diags.AddError(
			"RestoreRG: cannot restore resource group",
			err.Error(),
		)
		return diags
	}
	tflog.Info(ctx, "utilityRestoreRG: response from CloudAPI().RG().Restore", map[string]any{"rg_id": rgId, "response": res})

	return nil
}

// EnableRG performs resource group Enable request
func EnableRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *client.Client) error {
	enableReq := rg.EnableRequest{RGID: rgId}

	tflog.Info(ctx, "utilityEnableRG: before calling CloudAPI().RG().Enable", map[string]any{"rg_id": rgId, "req": enableReq})

	res, err := c.CloudAPI().RG().Enable(ctx, enableReq)

	tflog.Info(ctx, "utilityEnableRG: response from CloudAPI().RG().Enable", map[string]any{"rg_id": rgId, "response": res})

	return err
}

// DisableRG performs resource group Disable request
func DisableRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *client.Client) error {
	disableReq := rg.DisableRequest{RGID: rgId}
	tflog.Info(ctx, "utilityDisableRG: before calling CloudAPI().RG().Disable", map[string]any{"rg_id": rgId, "req": disableReq})

	res, err := c.CloudAPI().RG().Disable(ctx, disableReq)

	tflog.Info(ctx, "utilityDisableRG: response from CloudAPI().RG().Disable", map[string]any{"rg_id": rgId, "response": res})

	return err
}

// UpdateRG compares plan and state for resource group fields name, description, quota.
// If any changes are detected, Update request is performed. If not, no update is performed.
func UpdateRG(ctx context.Context, rgId uint64, plan, state *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	var updateNeeded bool
	var diags diag.Diagnostics

	updateReq := rg.UpdateRequest{
		RGID: rgId,
	}

	if !plan.Name.Equal(state.Name) {
		updateReq.Name = plan.Name.ValueString()
		tflog.Info(ctx, "utilityUpdateRG: new name specified", map[string]any{
			"rg_id":      plan.Id.ValueString(),
			"name_plan":  plan.Name.ValueString(),
			"name_state": state.Name.ValueString()})
		updateNeeded = true
	}

	if !plan.Description.Equal(state.Description) {
		updateReq.Description = plan.Description.ValueString()
		tflog.Info(ctx, "utilityUpdateRG: new description specified", map[string]any{
			"rg_id":             plan.Id.ValueString(),
			"description_plan":  plan.Description.ValueString(),
			"description_state": state.Description.ValueString()})
		updateNeeded = true
	}

	if !plan.UniqPools.Equal(state.UniqPools) {
		if plan.UniqPools.IsNull() || len(plan.UniqPools.Elements()) == 0 {
			updateReq.ClearUniqPools = true
		} else {
			uPoolsList := make([]string, 0, len(plan.UniqPools.Elements()))

			diags.Append(plan.UniqPools.ElementsAs(ctx, &uPoolsList, true)...)
			if diags.HasError() {
				tflog.Error(ctx, "utilityUpdateRG: cannot populate result with plan.UniqPools object element")
				return diags
			}
			updateReq.UniqPools = uPoolsList
		}
		updateNeeded = true
	}

	var updQuotaNeeded bool

	var quotaPlan, quotaState models.QuotaModel
	if !plan.Quota.IsNull() {
		diags = plan.Quota.As(ctx, &quotaPlan, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})
		if diags.HasError() {
			return diags
		}
	}
	if !state.Quota.IsNull() {
		diags = state.Quota.As(ctx, &quotaState, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})
		if diags.HasError() {
			return diags
		}
	}

	if !plan.Quota.IsNull() && !state.Quota.IsNull() {
		if !quotaPlan.CPU.Equal(quotaState.CPU) {
			updateReq.MaxCPUCapacity = quotaPlan.CPU.ValueInt64()
			updQuotaNeeded = true
		}
		if !quotaPlan.Disk.Equal(quotaState.Disk) {
			updateReq.MaxVDiskCapacity = quotaPlan.Disk.ValueInt64()
			updQuotaNeeded = true
		}
		if !quotaPlan.Ram.Equal(quotaState.Ram) {
			updateReq.MaxMemoryCapacity = quotaPlan.Ram.ValueInt64()
			updQuotaNeeded = true
		}
		if !quotaPlan.ExtTraffic.Equal(quotaState.ExtTraffic) {
			updateReq.MaxNetworkPeerTransfer = quotaPlan.ExtTraffic.ValueInt64()
			updQuotaNeeded = true
		}
		if !quotaPlan.ExtIps.Equal(quotaState.ExtIps) {
			updateReq.MaxNumPublicIP = quotaPlan.ExtIps.ValueInt64()
			updQuotaNeeded = true
		}
	}

	if state.Quota.IsNull() {
		if !quotaPlan.CPU.IsNull() {
			updateReq.MaxCPUCapacity = quotaPlan.CPU.ValueInt64()
		}
		if !quotaPlan.Disk.IsNull() {
			updateReq.MaxVDiskCapacity = quotaPlan.Disk.ValueInt64()
		}
		if !quotaPlan.Ram.IsNull() {
			updateReq.MaxMemoryCapacity = quotaPlan.Ram.ValueInt64()
		}
		if !quotaPlan.ExtTraffic.IsNull() {
			updateReq.MaxNetworkPeerTransfer = quotaPlan.ExtTraffic.ValueInt64()
		}
		if !quotaPlan.ExtIps.IsNull() {
			updateReq.MaxNumPublicIP = quotaPlan.ExtIps.ValueInt64()
		}
		updQuotaNeeded = true
	}
	// plan.Quota.IsNull() == true: we do not change quota in case it used to be set and then deleted

	if updQuotaNeeded {
		tflog.Info(ctx, "utilityUpdateRG: new quota specified", map[string]any{
			"rg_id": plan.Id.ValueString()})
		updateNeeded = true
	}

	if updateNeeded {
		tflog.Info(ctx, "utilityUpdateRG: before calling CloudAPI().RG().Update", map[string]any{"rg_id": plan.Id.ValueString(), "req": updateReq})
		res, err := c.CloudAPI().RG().Update(ctx, updateReq)
		tflog.Info(ctx, "utilityUpdateRG: response from CloudAPI().RG().Update", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
		if err != nil {
			diags.AddError("can not update RG", err.Error())
			return diags
		}
	}
	if !updateNeeded {
		tflog.Info(ctx, "utilityUpdateRG: call for CloudAPI().RG().Update was not needed", map[string]any{"rg_id": plan.Id.ValueString()})
	}

	return nil
}

// EnableDisableCreateRG performs Enable request is enable is true, and Disable request otherwise.
// In case of failure returns warnings.
func EnableDisableCreateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	diags := diag.Diagnostics{}

	var enable bool
	if plan.Enable.IsNull() {
		enable = true // default value
	} else {
		enable = plan.Enable.ValueBool()
	}

	tflog.Info(ctx, "EnableDisableCreateRG: resource group to be enabled/disabled", map[string]any{
		"rg_id":  rgId,
		"enable": enable})

	if enable {
		err := EnableRG(ctx, rgId, plan, c)
		if err != nil {
			diags.AddWarning(
				"EnableDisableCreateRG: cannot enable rg",
				err.Error(),
			)
			return diags
		}
	}

	if !enable {
		err := DisableRG(ctx, rgId, plan, c)
		if err != nil {
			diags.AddWarning(
				"EnableDisableCreateRG: cannot disable rg",
				err.Error(),
			)
			return diags
		}
	}

	tflog.Info(ctx, "EnableDisableCreateRG: resource group is successfully enabled/disabled", map[string]any{"rg_id": rgId, "enable": enable})
	return nil
}

// EnableDisableUpdateRG performs Enable request is enable is true, and Disable request otherwise.
// In case of failure returns errors.
func EnableDisableUpdateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	diags := diag.Diagnostics{}

	var enable bool
	if plan.Enable.IsNull() {
		enable = true // default value
	} else {
		enable = plan.Enable.ValueBool()
	}

	tflog.Info(ctx, "EnableDisableUpdateRG: resource group to be enabled/disabled", map[string]any{
		"rg_id":  rgId,
		"enable": enable})

	if enable {
		err := EnableRG(ctx, rgId, plan, c)
		if err != nil {
			diags.AddError(
				"EnableDisableUpdateRG: cannot enable rg",
				err.Error(),
			)
			return diags
		}
	}

	if !enable {
		err := DisableRG(ctx, rgId, plan, c)
		if err != nil {
			diags.AddError(
				"EnableDisableUpdateRG: cannot disable rg",
				err.Error(),
			)
			return diags
		}
	}

	tflog.Info(ctx, "EnableDisableUpdateRG: resource group is successfully enabled/disabled", map[string]any{"rg_id": rgId, "enable": enable})
	return nil
}

// AccessUpdateRG compares plan and state for resource group field access.
// If changes are detected, AccessRevoke request is performed for each deleted access user and AccessGrant request is
// performed for each added access user. If no changes are detected, no requests performed.
// Returns errors in case of failures.
func AccessUpdateRG(ctx context.Context, rgId uint64, plan, state *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	diags := diag.Diagnostics{}

	itemsAccessPlan := make([]models.AccessModel, 0, len(plan.Access.Elements()))
	diags = plan.Access.ElementsAs(ctx, &itemsAccessPlan, false)
	if diags.HasError() {
		tflog.Error(ctx, "AccessUpdateRG: cannot populate itemsAccess with plan.Access List elements")
		return diags
	}

	itemsAccessState := make([]models.AccessModel, 0, len(state.Access.Elements()))
	diags = state.Access.ElementsAs(ctx, &itemsAccessState, false)
	if diags.HasError() {
		tflog.Error(ctx, "AccessUpdateRG: cannot populate itemsAccess with state.Access List elements")
		return diags
	}

	// define accesses to be revoked and revoke them
	var deletedAccess []models.AccessModel
	for _, accessStateElem := range itemsAccessState {
		if !accessStateElem.Contains(itemsAccessPlan) {
			deletedAccess = append(deletedAccess, accessStateElem)
		}
	}

	if len(deletedAccess) == 0 {
		tflog.Info(ctx, "AccessUpdateRG: no access needs to be revoked", map[string]any{
			"rg_id": plan.Id.ValueString()})
	}
	if len(deletedAccess) > 0 {
		tflog.Info(ctx, "AccessUpdateRG: access needs to be revoked", map[string]any{
			"rg_id":          plan.Id.ValueString(),
			"deleted_access": deletedAccess})

		for _, deletedAccessItem := range deletedAccess {
			revokeReq := rg.AccessRevokeRequest{
				RGID: rgId,
				User: deletedAccessItem.User.ValueString(),
			}

			tflog.Info(ctx, "AccessUpdateRG: before calling CloudAPI().RG().AccessRevoke", map[string]any{"rg_id": plan.Id.ValueString(), "req": revokeReq})
			res, err := c.CloudAPI().RG().AccessRevoke(ctx, revokeReq)
			tflog.Info(ctx, "AccessUpdateRG: response from CloudAPI().RG().AccessRevoke", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
			if err != nil {
				diags.AddError(
					"AccessUpdateRG: cannot revoke access for rg",
					err.Error())
			}
		}
	}

	// define accesses to be granted and grant them
	var addedAccess []models.AccessModel
	for _, accessPlanElem := range itemsAccessPlan {
		if !accessPlanElem.Contains(itemsAccessState) {
			addedAccess = append(addedAccess, accessPlanElem)
		}
	}

	if len(addedAccess) == 0 {
		tflog.Info(ctx, "AccessUpdateRG: no access need to be granted", map[string]any{
			"rg_id": plan.Id.ValueString()})
	}
	if len(addedAccess) > 0 {
		tflog.Info(ctx, "AccessUpdateRG: access needs to be granted", map[string]any{
			"rg_id":        plan.Id.ValueString(),
			"added_access": addedAccess})

		for _, addedAccessItem := range addedAccess {
			grantReq := rg.AccessGrantRequest{
				RGID:  rgId,
				User:  addedAccessItem.User.ValueString(),
				Right: addedAccessItem.Right.ValueString(),
			}

			tflog.Info(ctx, "AccessUpdateRG: before calling CloudAPI().RG().AccessGrant", map[string]any{"rg_id": plan.Id.ValueString(), "req": grantReq})
			res, err := c.CloudAPI().RG().AccessGrant(ctx, grantReq)
			tflog.Info(ctx, "AccessUpdateRG: response from CloudAPI().RG().AccessGrant", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
			if err != nil {
				diags.AddError(
					"AccessUpdateRG: cannot grant access for rg",
					err.Error())
			}
		}
	}

	return diags
}

// AccessCreateRG grants access to users specified in access field for created resource.
// In case of failure returns warnings.
func AccessCreateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	diags := diag.Diagnostics{}

	if len(plan.Access.Elements()) != 0 {
		tflog.Info(ctx, "AccessCreateRG: access needs to be granted", map[string]any{
			"rg_id":       rgId,
			"access_plan": plan.Access.Elements()})

		itemsAccessPlan := make([]models.AccessModel, 0, len(plan.Access.Elements()))
		diagsItem := plan.Access.ElementsAs(ctx, &itemsAccessPlan, false)
		if diagsItem.HasError() {
			tflog.Warn(ctx, "cannot populate itemsAccess with plan.Access List elements")
			diags.AddWarning(fmt.Sprintf("AccessCreateRG: Unable to get access info for RG %d", rgId),
				"cannot populate itemsAccess with plan.Access List elements",
			)
			return diags
		}

		for _, addedAccessItem := range itemsAccessPlan {
			grantReq := rg.AccessGrantRequest{
				RGID:  rgId,
				User:  addedAccessItem.User.ValueString(),
				Right: addedAccessItem.Right.ValueString(),
			}

			tflog.Info(ctx, "AccessCreateRG: before calling CloudAPI().RG().AccessGrant", map[string]any{
				"rg_id": rgId,
				"req":   grantReq})
			res, err := c.CloudAPI().RG().AccessGrant(ctx, grantReq)
			if err != nil {
				diags.AddWarning("AccessCreateRG: Unable to grant access for RG",
					err.Error())
			}
			tflog.Info(ctx, "AccessCreateRG: response from CloudAPI().RG().AccessGrant", map[string]any{
				"rg_id":    rgId,
				"response": res})
		}
	}

	if len(plan.Access.Elements()) == 0 {
		tflog.Info(ctx, "AccessCreateRG: no access need to be granted", map[string]any{
			"rg_id":       rgId,
			"access_plan": plan.Access.Elements()})
	}

	return diags
}

// SetDefNetUpdateRG compares plan and state for resource group update field def_net.
// If any changes are detected, SetDefNet request is performed. If not, no SetDefNet is performed.
// Returns error in case of failures.
func SetDefNetUpdateRG(ctx context.Context, rgId uint64, plan, state *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	diags := diag.Diagnostics{}
	var setDefNetNeeded bool

	setDefNetReq := rg.SetDefNetRequest{
		RGID: rgId,
	}

	var itemDefNetPlan, itemDefNetState models.DefNetModel
	if !plan.DefNet.IsNull() {
		diags.Append(plan.DefNet.As(ctx, &itemDefNetPlan, basetypes.ObjectAsOptions{})...)
		if diags.HasError() {
			tflog.Error(ctx, "SetDefNetUpdateRG: cannot populate defNet with plan.DefNet object element")
			return diags
		}
	}
	if !state.DefNet.IsNull() {
		diags.Append(state.DefNet.As(ctx, &itemDefNetState, basetypes.ObjectAsOptions{})...)
		if diags.HasError() {
			tflog.Error(ctx, "SetDefNetUpdateRG: cannot populate defNet with state.DefNet object element")
			return diags
		}
	}

	if !plan.DefNet.IsNull() && !state.DefNet.IsNull() {
		if !itemDefNetPlan.NetId.Equal(itemDefNetState.NetId) {
			setDefNetNeeded = true
		}
		if !itemDefNetPlan.NetType.Equal(itemDefNetState.NetType) {
			setDefNetNeeded = true
		}

	} else {
		setDefNetNeeded = true
	}

	if setDefNetNeeded {
		tflog.Info(ctx, "utilitySetDefNetUpdateRG: new def_net specified", map[string]any{
			"rg_id":         plan.Id.ValueString(),
			"def_net_plan":  plan.DefNet,
			"def_net_state": state.DefNet})

		if plan.DefNet.IsNull() {
			removeReq := rg.RemoveDefNetRequest{RGID: uint64(state.RGID.ValueInt64())}
			res, err := c.CloudAPI().RG().RemoveDefNet(ctx, removeReq)
			if err != nil {
				diags.AddError(
					"SetDefNetUpdateRG: can not remove defNet for rg",
					err.Error())
				return diags
			}
			tflog.Info(ctx, "utilitySetDefNetUpdateRG: response from CloudAPI().RG().RemoveDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})
		} else {
			setDefNetReq.NetType = itemDefNetPlan.NetType.ValueString()
			if itemDefNetPlan.NetId.IsNull() {
				setDefNetReq.NetID = 0 // default value
			} else {
				setDefNetReq.NetID = uint64(itemDefNetPlan.NetId.ValueInt64())
			}

			tflog.Info(ctx, "utilitySetDefNetUpdateRG: before calling CloudAPI().RG().SetDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "req": setDefNetReq})
			res, err := c.CloudAPI().RG().SetDefNet(ctx, setDefNetReq)
			if err != nil {
				diags.AddError(
					"SetDefNetUpdateRG: can not set defNet for rg",
					err.Error())
				return diags
			}
			tflog.Info(ctx, "utilitySetDefNetUpdateRG: response from CloudAPI().RG().SetDefNet", map[string]any{"rg_id": plan.Id.ValueString(), "response": res})

		}
	}

	if !setDefNetNeeded {
		tflog.Info(ctx, "utilitySetDefNetUpdateRG: call for CloudAPI().RG().SetDefNet was not needed", map[string]any{
			"rg_id":         plan.Id.ValueString(),
			"def_net_plan":  plan.DefNet,
			"def_net_state": state.DefNet})
	}

	return nil
}

// SetDefNetCreateRG performs SetDefNet request if def_net field is not empty. Otherwise, no SetDefNet request is performed.
// In case of failure returns warnings.
func SetDefNetCreateRG(ctx context.Context, rgId uint64, plan *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	diags := diag.Diagnostics{}

	setDefNetReq := rg.SetDefNetRequest{RGID: rgId}

	var itemDefNetPlan models.DefNetModel

	if !plan.DefNet.IsNull() {
		tflog.Info(ctx, "SetDefNetCreateRG: new def_net specified", map[string]any{
			"rg_id":        rgId,
			"def_net_plan": plan.DefNet})

		diagItem := plan.DefNet.As(ctx, &itemDefNetPlan, basetypes.ObjectAsOptions{})
		if diagItem.HasError() {
			diags.AddWarning(
				fmt.Sprintf("SetDefNetCreateRG: Unable to setDefNet for RG %d", rgId),
				"cannot populate defNet with plan.DefNet object element for rg",
			)
			return diags
		}

		setDefNetReq.NetType = itemDefNetPlan.NetType.ValueString()
		if itemDefNetPlan.NetId.IsNull() {
			setDefNetReq.NetID = 0 // default value
		} else {
			setDefNetReq.NetID = uint64(itemDefNetPlan.NetId.ValueInt64())
		}

		tflog.Info(ctx, "SetDefNetCreateRG: before calling CloudAPI().RG().SetDefNet", map[string]any{"rg_id": rgId, "req": setDefNetReq})
		res, err := c.CloudAPI().RG().SetDefNet(ctx, setDefNetReq)
		tflog.Info(ctx, "SetDefNetCreateRG: response from CloudAPI().RG().SetDefNet", map[string]any{"rg_id": rgId, "response": res})
		if err != nil {
			diags.AddWarning(
				"SetDefNetCreateRG: Unable to setDefNet for RG",
				err.Error(),
			)
			return diags
		}
	}

	if plan.DefNet.IsNull() {
		tflog.Info(ctx, "SetDefNetCreateRG: call for CloudAPI().RG().SetDefNet was not needed", map[string]any{
			"rg_id":        rgId,
			"def_net_plan": plan.DefNet})
	}

	return nil
}

// RGReadStatus loads rg resource by ids id, gets it current status. Performs restore and enable if needed for
// Deleted status.
// In case of failure returns errors.
func RGReadStatus(ctx context.Context, state *models.ResourceRGModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "RGReadStatus: Read status rg with ID", map[string]any{"rg_id": state.Id.ValueString()})

	diags := diag.Diagnostics{}

	rgId, err := strconv.ParseUint(state.Id.ValueString(), 10, 64)
	if err != nil {
		diags.AddError("RGReadStatus: Cannot parse resource group ID from state", err.Error())
		return diags
	}

	recordRG, err := RGCheckPresence(ctx, rgId, c)
	if err != nil {
		diags.AddError("RGReadStatus: Unable to Read RG before status check", err.Error())
		return diags
	}
	tflog.Info(ctx, "RGReadStatus: resource group values before status check", map[string]any{
		"rg_id":            recordRG.ID,
		"updated_recordRG": recordRG})

	// check resource status
	switch recordRG.Status {
	case status.Modeled:
		diags.AddError(
			"RG is in status Modeled",
			"please, contact support for more information",
		)
		return diags
	case status.Deleted:
		tflog.Info(ctx, "RGReadStatus: resource group with status.Deleted is being checked", map[string]any{
			"rg_id":  recordRG.ID,
			"status": recordRG.Status})
		// restore and enable resource group in case it is required
		if state.Restore.IsNull() || state.Restore.ValueBool() { // default true or user set-up true
			diags.Append(RestoreRG(ctx, rgId, c)...)
			if diags.HasError() {
				tflog.Error(ctx, "RGReadStatus: cannot restore rg")
				return diags
			}
			tflog.Info(ctx, "RGReadStatus: resource group restored successfully", map[string]any{"rg_id": recordRG.ID})
			state.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))

			if state.Enable.IsNull() || state.Enable.ValueBool() { // default true or user set-up true
				err := EnableRG(ctx, rgId, state, c)
				if err != nil {
					diags.AddError(
						"RGReadStatus: Unable to Enable RG",
						err.Error(),
					)
					return diags
				}
				tflog.Info(ctx, "RGReadStatus: resource group enabled successfully", map[string]any{"rg_id": recordRG.ID})
			}
		}
	case status.Destroyed:
		diags.AddError(
			"RGReadStatus: RG is in status Destroyed",
			fmt.Sprintf("the resource with rg_id %d cannot be read because it has been destroyed", recordRG.ID),
		)
		return diags
	}

	return nil
}
