package policy

import (
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dynamodb"
)

func inputForRoleOwnersTable(config TableConfig, tableName string) *dynamodb.CreateTableInput {
	return &dynamodb.CreateTableInput{
		AttributeDefinitions: []*dynamodb.AttributeDefinition{
			{
				AttributeName: aws.String("rolename"),
				AttributeType: aws.String("S"),
			},
		},
		KeySchema: []*dynamodb.KeySchemaElement{
			{
				AttributeName: aws.String("rolename"),
				KeyType:       aws.String("HASH"),
			},
		},
		ProvisionedThroughput: &dynamodb.ProvisionedThroughput{
			ReadCapacityUnits:  aws.Int64(config.ThroughPutRead),
			WriteCapacityUnits: aws.Int64(config.ThroughPutWrite),
		},
		TableName: aws.String(tableName),
	}
}

// TableConfig contains reference to all required values for dynamoDB table creation
type TableConfig struct {
	ThroughPutRead  int64
	ThroughPutWrite int64
}

// CreateRoleOwnerTable does just that
// tablePrefix: prefix for table name, usually `sandstorm-ENV`
func (pg *IAMPolicyGenerator) CreateRoleOwnerTable(config TableConfig, tablePrefix string) error {
	tableName := RoleOwnerTable(tablePrefix)
	ok, err := pg.tableExists(tableName)
	if err != nil {
		return err
	}

	if ok {
		pg.Logger.Infof("RoleOwnerTable of name %s exists, exiting", tableName)
		return nil
	}
	pg.Logger.Infof("RoleOwnerTable of name %s not found, creating", tableName)
	input := inputForRoleOwnersTable(config, tableName)
	_, err = pg.DynamoDB.CreateTable(input)
	if err != nil {
		return err
	}

	// Verify the table is reachable/usable before exiting function
	var timeout time.Duration
	timeout = 1
	for {
		describeInput := &dynamodb.DescribeTableInput{
			TableName: aws.String(tableName),
		}
		describeOutput, err := pg.DynamoDB.DescribeTable(describeInput)
		if err != nil {
			return err
		}

		if *describeOutput.Table.TableStatus == "ACTIVE" {
			return nil
		}

		time.Sleep(timeout * time.Second)
		if timeout < 8 {
			timeout = timeout * 2
		}
	}
}

func (pg *IAMPolicyGenerator) tableExists(tableName string) (bool, error) {
	var lastTable *string
	for {
		params := &dynamodb.ListTablesInput{}
		if lastTable != nil {
			params.ExclusiveStartTableName = lastTable
		}
		out, err := pg.DynamoDB.ListTables(params)
		if err != nil {
			return false, err
		}
		for _, table := range out.TableNames {
			if *table == tableName {
				return true, nil
			}
		}
		if out.LastEvaluatedTableName == nil {
			return false, nil
		}
		lastTable = out.LastEvaluatedTableName
	}
}
