// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"aws-step-functions-long-lived-transactions/models"

	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
	"github.com/aws/aws-xray-sdk-go/xray"
)

var dynamoDB *dynamodb.DynamoDB

func init() {

	// create DynamoDB client
	var awscfg = &aws.Config{
		Region: aws.String(os.Getenv("AWS_REGION")),
	}
	var sess = session.Must(session.NewSession(awscfg))
	dynamoDB = dynamodb.New(sess)

	// AWS X-Ray for AWS SDK trace
	xray.AWS(dynamoDB.Client)

	log.SetPrefix("TRACE: ")
	log.SetFlags(log.Ldate | log.Ltime)

}

func handler(ctx context.Context, ord models.Order) (models.Order, error) {

	log.Printf("[%s] - processing refund", ord.OrderID)

	// find Payment transaction for this order
	payment, err := getTransaction(ctx, ord.OrderID)
	if err != nil {
		log.Printf("[%s] - error! %s", ord.OrderID, err.Error())
		return ord, models.NewErrProcessRefund(err.Error())
	}

	// process the refund for the order
	payment.Refund()

	// write to database.
	err = saveTransaction(ctx, payment)
	if err != nil {
		log.Printf("[%s] - error! %s", ord.OrderID, err.Error())
		return ord, models.NewErrProcessRefund(err.Error())
	}

	// save state
	ord.Payment = payment

	// testing scenario
	if ord.OrderID[0:2] == "22" {
		return ord, models.NewErrProcessRefund("Unable to process refund for order " + ord.OrderID)
	}

	log.Printf("[%s] - refund processed", ord.OrderID)

	return ord, nil
}

func main() {
	lambda.Start(handler)
}

// returns a specified payment transaction from the database
func getTransaction(ctx context.Context, orderID string) (models.Payment, error) {

	payment := models.Payment{}

	input := &dynamodb.QueryInput{
		ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
			":v1": {
				S: aws.String(orderID),
			},
			":v2": {
				S: aws.String("Debit"),
			},
		},
		KeyConditionExpression: aws.String("order_id = :v1 AND payment_type = :v2"),
		TableName:              aws.String(os.Getenv("TABLE_NAME")),
		IndexName:              aws.String("orderIDIndex"),
	}

	// Get payment transaction from database
	result, err := dynamoDB.QueryWithContext(ctx, input)
	if err != nil {
		return payment, err
	}

	err = dynamodbattribute.UnmarshalMap(result.Items[0], &payment)
	if err != nil {
		return payment, fmt.Errorf("failed to DynamoDB unmarshal Payment, %v", err)
	}

	return payment, nil
}

// saves refund transaction to the database
func saveTransaction(ctx context.Context, payment models.Payment) error {

	marshalledPaymentTransaction, err := dynamodbattribute.MarshalMap(payment)
	if err != nil {
		return fmt.Errorf("failed to DynamoDB marshal Payment, %v", err)
	}

	_, err = dynamoDB.PutItemWithContext(ctx, &dynamodb.PutItemInput{
		TableName: aws.String(os.Getenv("TABLE_NAME")),
		Item:      marshalledPaymentTransaction,
	})

	if err != nil {
		return fmt.Errorf("failed to put record to DynamoDB, %v", err)
	}
	return nil
}
