package dynamodb

import (
	"errors"
	"reflect"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface"
	"github.com/containous/traefik/types"
)

type mockDynamoDBClient struct {
	dynamodbiface.DynamoDBAPI
	testWithError bool
}

var backend = &types.Backend{
	HealthCheck: &types.HealthCheck{
		Path: "/build",
	},
	Servers: map[string]types.Server{
		"server1": {
			URL: "http://test.traefik.io",
		},
	},
}

var frontend = &types.Frontend{
	EntryPoints: []string{"http"},
	Backend:     "test.traefik.io",
	Routes: map[string]types.Route{
		"route1": {
			Rule: "Host:test.traefik.io",
		},
	},
}

// ScanPages simulates a call to ScanPages (see https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/#DynamoDB.ScanPages)
// by running the fn function twice and returning an item each time.
func (m *mockDynamoDBClient) ScanPages(input *dynamodb.ScanInput, fn func(*dynamodb.ScanOutput, bool) bool) error {
	if m.testWithError {
		return errors.New("fake error")
	}
	attributeBackend, err := dynamodbattribute.Marshal(backend)
	if err != nil {
		return err
	}

	attributeFrontend, err := dynamodbattribute.Marshal(frontend)
	if err != nil {
		return err
	}

	fn(&dynamodb.ScanOutput{
		Items: []map[string]*dynamodb.AttributeValue{
			{
				"id": &dynamodb.AttributeValue{
					S: aws.String("test.traefik.io_backend"),
				},
				"name": &dynamodb.AttributeValue{
					S: aws.String("test.traefik.io"),
				},
				"backend": attributeBackend,
			},
		},
	}, false)

	fn(&dynamodb.ScanOutput{
		Items: []map[string]*dynamodb.AttributeValue{
			{
				"id": &dynamodb.AttributeValue{
					S: aws.String("test.traefik.io_frontend"),
				},
				"name": &dynamodb.AttributeValue{
					S: aws.String("test.traefik.io"),
				},
				"frontend": attributeFrontend,
			},
		},
	}, true)
	return nil
}

func TestLoadDynamoConfigSuccessful(t *testing.T) {
	dbiface := &dynamoClient{
		db: &mockDynamoDBClient{
			testWithError: false,
		},
	}
	provider := Provider{}
	loadedConfig, err := provider.loadDynamoConfig(dbiface)
	if err != nil {
		t.Fatal(err)
	}
	expectedConfig := &types.Configuration{
		Backends: map[string]*types.Backend{
			"test.traefik.io": backend,
		},
		Frontends: map[string]*types.Frontend{
			"test.traefik.io": frontend,
		},
	}
	if !reflect.DeepEqual(loadedConfig, expectedConfig) {
		t.Fatalf("Configurations did not match: %v %v", loadedConfig, expectedConfig)
	}
}

func TestLoadDynamoConfigFailure(t *testing.T) {
	dbiface := &dynamoClient{
		db: &mockDynamoDBClient{
			testWithError: true,
		},
	}
	provider := Provider{}
	_, err := provider.loadDynamoConfig(dbiface)
	if err == nil {
		t.Fatal("Expected error")
	}
}

func TestCreateClientSuccessful(t *testing.T) {
	provider := Provider{
		Region: "us-east-1",
	}
	_, err := provider.createClient()
	if err != nil {
		t.Fatal(err)
	}
}

func TestCreateClientFailure(t *testing.T) {
	provider := Provider{}
	_, err := provider.createClient()
	if err == nil {
		t.Fatal("Expected error")
	}
}
