// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//     http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

// Code generated by ack-generate. DO NOT EDIT.

package db_cluster_snapshot

import (
	"context"
	"errors"
	"fmt"
	"reflect"
	"strings"

	ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1"
	ackcompare "github.com/aws-controllers-k8s/runtime/pkg/compare"
	ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition"
	ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors"
	ackrequeue "github.com/aws-controllers-k8s/runtime/pkg/requeue"
	ackrtlog "github.com/aws-controllers-k8s/runtime/pkg/runtime/log"
	"github.com/aws/aws-sdk-go-v2/aws"
	svcsdk "github.com/aws/aws-sdk-go-v2/service/rds"
	svcsdktypes "github.com/aws/aws-sdk-go-v2/service/rds/types"
	smithy "github.com/aws/smithy-go"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	svcapitypes "github.com/aws-controllers-k8s/rds-controller/apis/v1alpha1"
)

// Hack to avoid import errors during build...
var (
	_ = &metav1.Time{}
	_ = strings.ToLower("")
	_ = &svcsdk.Client{}
	_ = &svcapitypes.DBClusterSnapshot{}
	_ = ackv1alpha1.AWSAccountID("")
	_ = &ackerr.NotFound
	_ = &ackcondition.NotManagedMessage
	_ = &reflect.Value{}
	_ = fmt.Sprintf("")
	_ = &ackrequeue.NoRequeue{}
	_ = &aws.Config{}
)

// sdkFind returns SDK-specific information about a supplied resource
func (rm *resourceManager) sdkFind(
	ctx context.Context,
	r *resource,
) (latest *resource, err error) {
	rlog := ackrtlog.FromContext(ctx)
	exit := rlog.Trace("rm.sdkFind")
	defer func() {
		exit(err)
	}()
	// If any required fields in the input shape are missing, AWS resource is
	// not created yet. Return NotFound here to indicate to callers that the
	// resource isn't yet created.
	if rm.requiredFieldsMissingFromReadManyInput(r) {
		return nil, ackerr.NotFound
	}

	input, err := rm.newListRequestPayload(r)
	if err != nil {
		return nil, err
	}
	var resp *svcsdk.DescribeDBClusterSnapshotsOutput
	resp, err = rm.sdkapi.DescribeDBClusterSnapshots(ctx, input)
	rm.metrics.RecordAPICall("READ_MANY", "DescribeDBClusterSnapshots", err)
	if err != nil {
		var awsErr smithy.APIError
		if errors.As(err, &awsErr) && awsErr.ErrorCode() == "DBClusterSnapshotNotFoundFault" {
			return nil, ackerr.NotFound
		}
		return nil, err
	}

	// Merge in the information we read from the API call above to the copy of
	// the original Kubernetes object we passed to the function
	ko := r.ko.DeepCopy()

	found := false
	for _, elem := range resp.DBClusterSnapshots {
		if elem.AllocatedStorage != nil {
			allocatedStorageCopy := int64(*elem.AllocatedStorage)
			ko.Status.AllocatedStorage = &allocatedStorageCopy
		} else {
			ko.Status.AllocatedStorage = nil
		}
		if elem.AvailabilityZones != nil {
			ko.Status.AvailabilityZones = aws.StringSlice(elem.AvailabilityZones)
		} else {
			ko.Status.AvailabilityZones = nil
		}
		if elem.ClusterCreateTime != nil {
			ko.Status.ClusterCreateTime = &metav1.Time{*elem.ClusterCreateTime}
		} else {
			ko.Status.ClusterCreateTime = nil
		}
		if elem.DBClusterIdentifier != nil {
			ko.Spec.DBClusterIdentifier = elem.DBClusterIdentifier
		} else {
			ko.Spec.DBClusterIdentifier = nil
		}
		if elem.DBClusterSnapshotArn != nil {
			if ko.Status.ACKResourceMetadata == nil {
				ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}
			}
			tmpARN := ackv1alpha1.AWSResourceName(*elem.DBClusterSnapshotArn)
			ko.Status.ACKResourceMetadata.ARN = &tmpARN
		}
		if elem.DBClusterSnapshotIdentifier != nil {
			ko.Spec.DBClusterSnapshotIdentifier = elem.DBClusterSnapshotIdentifier
		} else {
			ko.Spec.DBClusterSnapshotIdentifier = nil
		}
		if elem.DBSystemId != nil {
			ko.Status.DBSystemID = elem.DBSystemId
		} else {
			ko.Status.DBSystemID = nil
		}
		if elem.Engine != nil {
			ko.Status.Engine = elem.Engine
		} else {
			ko.Status.Engine = nil
		}
		if elem.EngineMode != nil {
			ko.Status.EngineMode = elem.EngineMode
		} else {
			ko.Status.EngineMode = nil
		}
		if elem.EngineVersion != nil {
			ko.Status.EngineVersion = elem.EngineVersion
		} else {
			ko.Status.EngineVersion = nil
		}
		if elem.IAMDatabaseAuthenticationEnabled != nil {
			ko.Status.IAMDatabaseAuthenticationEnabled = elem.IAMDatabaseAuthenticationEnabled
		} else {
			ko.Status.IAMDatabaseAuthenticationEnabled = nil
		}
		if elem.KmsKeyId != nil {
			ko.Status.KMSKeyID = elem.KmsKeyId
		} else {
			ko.Status.KMSKeyID = nil
		}
		if elem.LicenseModel != nil {
			ko.Status.LicenseModel = elem.LicenseModel
		} else {
			ko.Status.LicenseModel = nil
		}
		if elem.MasterUsername != nil {
			ko.Status.MasterUsername = elem.MasterUsername
		} else {
			ko.Status.MasterUsername = nil
		}
		if elem.PercentProgress != nil {
			percentProgressCopy := int64(*elem.PercentProgress)
			ko.Status.PercentProgress = &percentProgressCopy
		} else {
			ko.Status.PercentProgress = nil
		}
		if elem.Port != nil {
			portCopy := int64(*elem.Port)
			ko.Status.Port = &portCopy
		} else {
			ko.Status.Port = nil
		}
		if elem.SnapshotCreateTime != nil {
			ko.Status.SnapshotCreateTime = &metav1.Time{*elem.SnapshotCreateTime}
		} else {
			ko.Status.SnapshotCreateTime = nil
		}
		if elem.SnapshotType != nil {
			ko.Status.SnapshotType = elem.SnapshotType
		} else {
			ko.Status.SnapshotType = nil
		}
		if elem.SourceDBClusterSnapshotArn != nil {
			ko.Status.SourceDBClusterSnapshotARN = elem.SourceDBClusterSnapshotArn
		} else {
			ko.Status.SourceDBClusterSnapshotARN = nil
		}
		if elem.Status != nil {
			ko.Status.Status = elem.Status
		} else {
			ko.Status.Status = nil
		}
		if elem.StorageEncrypted != nil {
			ko.Status.StorageEncrypted = elem.StorageEncrypted
		} else {
			ko.Status.StorageEncrypted = nil
		}
		if elem.TagList != nil {
			f21 := []*svcapitypes.Tag{}
			for _, f21iter := range elem.TagList {
				f21elem := &svcapitypes.Tag{}
				if f21iter.Key != nil {
					f21elem.Key = f21iter.Key
				}
				if f21iter.Value != nil {
					f21elem.Value = f21iter.Value
				}
				f21 = append(f21, f21elem)
			}
			ko.Status.TagList = f21
		} else {
			ko.Status.TagList = nil
		}
		if elem.VpcId != nil {
			ko.Status.VPCID = elem.VpcId
		} else {
			ko.Status.VPCID = nil
		}
		found = true
		break
	}
	if !found {
		return nil, ackerr.NotFound
	}

	rm.setStatusDefaults(ko)
	if ko.Status.ACKResourceMetadata != nil && ko.Status.ACKResourceMetadata.ARN != nil {
		resourceARN := (*string)(ko.Status.ACKResourceMetadata.ARN)
		tags, err := rm.getTags(ctx, *resourceARN)
		if err != nil {
			return nil, err
		}
		ko.Spec.Tags = tags
	}
	if !clusterSnapshotAvailable(&resource{ko}) {
		// Setting resource synced condition to false will trigger a requeue of
		// the resource. No need to return a requeue error here.
		ackcondition.SetSynced(&resource{ko}, corev1.ConditionFalse, nil, nil)
	}

	return &resource{ko}, nil
}

// requiredFieldsMissingFromReadManyInput returns true if there are any fields
// for the ReadMany Input shape that are required but not present in the
// resource's Spec or Status
func (rm *resourceManager) requiredFieldsMissingFromReadManyInput(
	r *resource,
) bool {
	return false
}

// newListRequestPayload returns SDK-specific struct for the HTTP request
// payload of the List API call for the resource
func (rm *resourceManager) newListRequestPayload(
	r *resource,
) (*svcsdk.DescribeDBClusterSnapshotsInput, error) {
	res := &svcsdk.DescribeDBClusterSnapshotsInput{}

	if r.ko.Spec.DBClusterIdentifier != nil {
		res.DBClusterIdentifier = r.ko.Spec.DBClusterIdentifier
	}
	if r.ko.Spec.DBClusterSnapshotIdentifier != nil {
		res.DBClusterSnapshotIdentifier = r.ko.Spec.DBClusterSnapshotIdentifier
	}
	if r.ko.Status.SnapshotType != nil {
		res.SnapshotType = r.ko.Status.SnapshotType
	}

	return res, nil
}

// sdkCreate creates the supplied resource in the backend AWS service API and
// returns a copy of the resource with resource fields (in both Spec and
// Status) filled in with values from the CREATE API operation's Output shape.
func (rm *resourceManager) sdkCreate(
	ctx context.Context,
	desired *resource,
) (created *resource, err error) {
	rlog := ackrtlog.FromContext(ctx)
	exit := rlog.Trace("rm.sdkCreate")
	defer func() {
		exit(err)
	}()
	input, err := rm.newCreateRequestPayload(ctx, desired)
	if err != nil {
		return nil, err
	}

	var resp *svcsdk.CreateDBClusterSnapshotOutput
	_ = resp
	resp, err = rm.sdkapi.CreateDBClusterSnapshot(ctx, input)
	rm.metrics.RecordAPICall("CREATE", "CreateDBClusterSnapshot", err)
	if err != nil {
		return nil, err
	}
	// Merge in the information we read from the API call above to the copy of
	// the original Kubernetes object we passed to the function
	ko := desired.ko.DeepCopy()

	if resp.DBClusterSnapshot.AllocatedStorage != nil {
		allocatedStorageCopy := int64(*resp.DBClusterSnapshot.AllocatedStorage)
		ko.Status.AllocatedStorage = &allocatedStorageCopy
	} else {
		ko.Status.AllocatedStorage = nil
	}
	if resp.DBClusterSnapshot.AvailabilityZones != nil {
		ko.Status.AvailabilityZones = aws.StringSlice(resp.DBClusterSnapshot.AvailabilityZones)
	} else {
		ko.Status.AvailabilityZones = nil
	}
	if resp.DBClusterSnapshot.ClusterCreateTime != nil {
		ko.Status.ClusterCreateTime = &metav1.Time{*resp.DBClusterSnapshot.ClusterCreateTime}
	} else {
		ko.Status.ClusterCreateTime = nil
	}
	if resp.DBClusterSnapshot.DBClusterIdentifier != nil {
		ko.Spec.DBClusterIdentifier = resp.DBClusterSnapshot.DBClusterIdentifier
	} else {
		ko.Spec.DBClusterIdentifier = nil
	}
	if ko.Status.ACKResourceMetadata == nil {
		ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}
	}
	if resp.DBClusterSnapshot.DBClusterSnapshotArn != nil {
		arn := ackv1alpha1.AWSResourceName(*resp.DBClusterSnapshot.DBClusterSnapshotArn)
		ko.Status.ACKResourceMetadata.ARN = &arn
	}
	if resp.DBClusterSnapshot.DBClusterSnapshotIdentifier != nil {
		ko.Spec.DBClusterSnapshotIdentifier = resp.DBClusterSnapshot.DBClusterSnapshotIdentifier
	} else {
		ko.Spec.DBClusterSnapshotIdentifier = nil
	}
	if resp.DBClusterSnapshot.DBSystemId != nil {
		ko.Status.DBSystemID = resp.DBClusterSnapshot.DBSystemId
	} else {
		ko.Status.DBSystemID = nil
	}
	if resp.DBClusterSnapshot.Engine != nil {
		ko.Status.Engine = resp.DBClusterSnapshot.Engine
	} else {
		ko.Status.Engine = nil
	}
	if resp.DBClusterSnapshot.EngineMode != nil {
		ko.Status.EngineMode = resp.DBClusterSnapshot.EngineMode
	} else {
		ko.Status.EngineMode = nil
	}
	if resp.DBClusterSnapshot.EngineVersion != nil {
		ko.Status.EngineVersion = resp.DBClusterSnapshot.EngineVersion
	} else {
		ko.Status.EngineVersion = nil
	}
	if resp.DBClusterSnapshot.IAMDatabaseAuthenticationEnabled != nil {
		ko.Status.IAMDatabaseAuthenticationEnabled = resp.DBClusterSnapshot.IAMDatabaseAuthenticationEnabled
	} else {
		ko.Status.IAMDatabaseAuthenticationEnabled = nil
	}
	if resp.DBClusterSnapshot.KmsKeyId != nil {
		ko.Status.KMSKeyID = resp.DBClusterSnapshot.KmsKeyId
	} else {
		ko.Status.KMSKeyID = nil
	}
	if resp.DBClusterSnapshot.LicenseModel != nil {
		ko.Status.LicenseModel = resp.DBClusterSnapshot.LicenseModel
	} else {
		ko.Status.LicenseModel = nil
	}
	if resp.DBClusterSnapshot.MasterUsername != nil {
		ko.Status.MasterUsername = resp.DBClusterSnapshot.MasterUsername
	} else {
		ko.Status.MasterUsername = nil
	}
	if resp.DBClusterSnapshot.PercentProgress != nil {
		percentProgressCopy := int64(*resp.DBClusterSnapshot.PercentProgress)
		ko.Status.PercentProgress = &percentProgressCopy
	} else {
		ko.Status.PercentProgress = nil
	}
	if resp.DBClusterSnapshot.Port != nil {
		portCopy := int64(*resp.DBClusterSnapshot.Port)
		ko.Status.Port = &portCopy
	} else {
		ko.Status.Port = nil
	}
	if resp.DBClusterSnapshot.SnapshotCreateTime != nil {
		ko.Status.SnapshotCreateTime = &metav1.Time{*resp.DBClusterSnapshot.SnapshotCreateTime}
	} else {
		ko.Status.SnapshotCreateTime = nil
	}
	if resp.DBClusterSnapshot.SnapshotType != nil {
		ko.Status.SnapshotType = resp.DBClusterSnapshot.SnapshotType
	} else {
		ko.Status.SnapshotType = nil
	}
	if resp.DBClusterSnapshot.SourceDBClusterSnapshotArn != nil {
		ko.Status.SourceDBClusterSnapshotARN = resp.DBClusterSnapshot.SourceDBClusterSnapshotArn
	} else {
		ko.Status.SourceDBClusterSnapshotARN = nil
	}
	if resp.DBClusterSnapshot.Status != nil {
		ko.Status.Status = resp.DBClusterSnapshot.Status
	} else {
		ko.Status.Status = nil
	}
	if resp.DBClusterSnapshot.StorageEncrypted != nil {
		ko.Status.StorageEncrypted = resp.DBClusterSnapshot.StorageEncrypted
	} else {
		ko.Status.StorageEncrypted = nil
	}
	if resp.DBClusterSnapshot.TagList != nil {
		f21 := []*svcapitypes.Tag{}
		for _, f21iter := range resp.DBClusterSnapshot.TagList {
			f21elem := &svcapitypes.Tag{}
			if f21iter.Key != nil {
				f21elem.Key = f21iter.Key
			}
			if f21iter.Value != nil {
				f21elem.Value = f21iter.Value
			}
			f21 = append(f21, f21elem)
		}
		ko.Status.TagList = f21
	} else {
		ko.Status.TagList = nil
	}
	if resp.DBClusterSnapshot.VpcId != nil {
		ko.Status.VPCID = resp.DBClusterSnapshot.VpcId
	} else {
		ko.Status.VPCID = nil
	}

	rm.setStatusDefaults(ko)
	r := &resource{ko}
	// We expect the DB cluster snapshot to be in 'creating' status since we just
	// issued the call to create it, but I suppose it doesn't hurt to check here.
	if clusterSnapshotCreating(r) {
		// Setting resource synced condition to false will trigger a requeue of
		// the resource. No need to return a requeue error here.
		ackcondition.SetSynced(r, corev1.ConditionFalse, nil, nil)
		return r, nil
	}
	return &resource{ko}, nil
}

// newCreateRequestPayload returns an SDK-specific struct for the HTTP request
// payload of the Create API call for the resource
func (rm *resourceManager) newCreateRequestPayload(
	ctx context.Context,
	r *resource,
) (*svcsdk.CreateDBClusterSnapshotInput, error) {
	res := &svcsdk.CreateDBClusterSnapshotInput{}

	if r.ko.Spec.DBClusterIdentifier != nil {
		res.DBClusterIdentifier = r.ko.Spec.DBClusterIdentifier
	}
	if r.ko.Spec.DBClusterSnapshotIdentifier != nil {
		res.DBClusterSnapshotIdentifier = r.ko.Spec.DBClusterSnapshotIdentifier
	}
	if r.ko.Spec.Tags != nil {
		f2 := []svcsdktypes.Tag{}
		for _, f2iter := range r.ko.Spec.Tags {
			f2elem := &svcsdktypes.Tag{}
			if f2iter.Key != nil {
				f2elem.Key = f2iter.Key
			}
			if f2iter.Value != nil {
				f2elem.Value = f2iter.Value
			}
			f2 = append(f2, *f2elem)
		}
		res.Tags = f2
	}

	return res, nil
}

// sdkUpdate patches the supplied resource in the backend AWS service API and
// returns a new resource with updated fields.
func (rm *resourceManager) sdkUpdate(
	ctx context.Context,
	desired *resource,
	latest *resource,
	delta *ackcompare.Delta,
) (*resource, error) {
	return rm.customUpdateDBClusterSnapshot(ctx, desired, latest, delta)
}

// sdkDelete deletes the supplied resource in the backend AWS service API
func (rm *resourceManager) sdkDelete(
	ctx context.Context,
	r *resource,
) (latest *resource, err error) {
	rlog := ackrtlog.FromContext(ctx)
	exit := rlog.Trace("rm.sdkDelete")
	defer func() {
		exit(err)
	}()
	input, err := rm.newDeleteRequestPayload(r)
	if err != nil {
		return nil, err
	}
	var resp *svcsdk.DeleteDBClusterSnapshotOutput
	_ = resp
	resp, err = rm.sdkapi.DeleteDBClusterSnapshot(ctx, input)
	rm.metrics.RecordAPICall("DELETE", "DeleteDBClusterSnapshot", err)
	return nil, err
}

// newDeleteRequestPayload returns an SDK-specific struct for the HTTP request
// payload of the Delete API call for the resource
func (rm *resourceManager) newDeleteRequestPayload(
	r *resource,
) (*svcsdk.DeleteDBClusterSnapshotInput, error) {
	res := &svcsdk.DeleteDBClusterSnapshotInput{}

	if r.ko.Spec.DBClusterSnapshotIdentifier != nil {
		res.DBClusterSnapshotIdentifier = r.ko.Spec.DBClusterSnapshotIdentifier
	}

	return res, nil
}

// setStatusDefaults sets default properties into supplied custom resource
func (rm *resourceManager) setStatusDefaults(
	ko *svcapitypes.DBClusterSnapshot,
) {
	if ko.Status.ACKResourceMetadata == nil {
		ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}
	}
	if ko.Status.ACKResourceMetadata.Region == nil {
		ko.Status.ACKResourceMetadata.Region = &rm.awsRegion
	}
	if ko.Status.ACKResourceMetadata.OwnerAccountID == nil {
		ko.Status.ACKResourceMetadata.OwnerAccountID = &rm.awsAccountID
	}
	if ko.Status.Conditions == nil {
		ko.Status.Conditions = []*ackv1alpha1.Condition{}
	}
}

// updateConditions returns updated resource, true; if conditions were updated
// else it returns nil, false
func (rm *resourceManager) updateConditions(
	r *resource,
	onSuccess bool,
	err error,
) (*resource, bool) {
	ko := r.ko.DeepCopy()
	rm.setStatusDefaults(ko)

	// Terminal condition
	var terminalCondition *ackv1alpha1.Condition = nil
	var recoverableCondition *ackv1alpha1.Condition = nil
	var syncCondition *ackv1alpha1.Condition = nil
	for _, condition := range ko.Status.Conditions {
		if condition.Type == ackv1alpha1.ConditionTypeTerminal {
			terminalCondition = condition
		}
		if condition.Type == ackv1alpha1.ConditionTypeRecoverable {
			recoverableCondition = condition
		}
		if condition.Type == ackv1alpha1.ConditionTypeResourceSynced {
			syncCondition = condition
		}
	}
	var termError *ackerr.TerminalError
	if rm.terminalAWSError(err) || err == ackerr.SecretTypeNotSupported || err == ackerr.SecretNotFound || errors.As(err, &termError) {
		if terminalCondition == nil {
			terminalCondition = &ackv1alpha1.Condition{
				Type: ackv1alpha1.ConditionTypeTerminal,
			}
			ko.Status.Conditions = append(ko.Status.Conditions, terminalCondition)
		}
		var errorMessage = ""
		if err == ackerr.SecretTypeNotSupported || err == ackerr.SecretNotFound || errors.As(err, &termError) {
			errorMessage = err.Error()
		} else {
			awsErr, _ := ackerr.AWSError(err)
			errorMessage = awsErr.Error()
		}
		terminalCondition.Status = corev1.ConditionTrue
		terminalCondition.Message = &errorMessage
	} else {
		// Clear the terminal condition if no longer present
		if terminalCondition != nil {
			terminalCondition.Status = corev1.ConditionFalse
			terminalCondition.Message = nil
		}
		// Handling Recoverable Conditions
		if err != nil {
			if recoverableCondition == nil {
				// Add a new Condition containing a non-terminal error
				recoverableCondition = &ackv1alpha1.Condition{
					Type: ackv1alpha1.ConditionTypeRecoverable,
				}
				ko.Status.Conditions = append(ko.Status.Conditions, recoverableCondition)
			}
			recoverableCondition.Status = corev1.ConditionTrue
			awsErr, _ := ackerr.AWSError(err)
			errorMessage := err.Error()
			if awsErr != nil {
				errorMessage = awsErr.Error()
			}
			recoverableCondition.Message = &errorMessage
		} else if recoverableCondition != nil {
			recoverableCondition.Status = corev1.ConditionFalse
			recoverableCondition.Message = nil
		}
	}
	// Required to avoid the "declared but not used" error in the default case
	_ = syncCondition
	if terminalCondition != nil || recoverableCondition != nil || syncCondition != nil {
		return &resource{ko}, true // updated
	}
	return nil, false // not updated
}

// terminalAWSError returns awserr, true; if the supplied error is an aws Error type
// and if the exception indicates that it is a Terminal exception
// 'Terminal' exception are specified in generator configuration
func (rm *resourceManager) terminalAWSError(err error) bool {
	// No terminal_errors specified for this resource in generator config
	return false
}
