package guardian

import (
	"errors"
	"fmt"
	"reflect"

	"code.justin.tv/systems/guardian/osin"

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

// SaveClient saves a client to dynamodb
func (db *Storage) SaveClient(client osin.Client) (err error) {
	c, ok := client.(*Client)
	if !ok {
		err = errors.New("guardian: client is not of type *Client")
	}

	item, err := dynamodbattribute.ConvertToMap(*c)
	if err != nil {
		return
	}

	delete(item, "secret")

	params := &dynamodb.PutItemInput{
		TableName: aws.String(db.Config.ClientsTable),
		Item:      item,
	}
	_, err = db.DB.PutItem(params)
	if err != nil {
		return
	}
	return
}

// GetClient retrieves a client by ID from dynamodb
// If no item is found, return nil client
func (db *Storage) GetClient(id string) (client osin.Client, err error) {
	if id == "" {
		err = errors.New("guardian: id required when getting client")
		return
	}

	params := &dynamodb.GetItemInput{
		TableName: aws.String(db.Config.ClientsTable),
		Key: map[string]*dynamodb.AttributeValue{
			"id": {S: aws.String(id)},
		},
		ConsistentRead: aws.Bool(true),
	}

	output, err := db.DB.GetItem(params)
	if err != nil {
		err = fmt.Errorf("Error retrieving client from Dynamo: %s", err.Error())
		return
	}

	if len(output.Item) == 0 {
		return nil, nil
	}

	c := &Client{}
	err = dynamodbattribute.ConvertFromMap(output.Item, c)
	if err != nil {
		err = fmt.Errorf("Error building client from Dynamo: %s", err.Error())
		return
	}

	var emptyClient Client
	if !reflect.DeepEqual(*c, emptyClient) {
		client = c
	}
	return
}

// DeleteClient deletes a client by ID from dynamodb
func (db *Storage) DeleteClient(client osin.Client) (err error) {
	if client.GetID() == "" {
		err = errors.New("guardian: id required when deleting client")
		return
	}

	return db.DeleteClientByID(client.GetID())
}

// DeleteClientByID deletes the client from dynamodb
func (db *Storage) DeleteClientByID(id string) (err error) {
	params := &dynamodb.DeleteItemInput{
		TableName: aws.String(db.Config.ClientsTable),
		Key: map[string]*dynamodb.AttributeValue{
			"id": {S: aws.String(id)},
		},
	}
	_, err = db.DB.DeleteItem(params)
	return
}

// ListClients returns a list of clients.
// startID can be empty, limit enables pagination, use 0 for all
func (db *Storage) ListClients(startID string, limit int64) (clients []osin.Client, err error) {
	params := &dynamodb.ScanInput{
		TableName: aws.String(db.Config.ClientsTable),
	}

	if limit != 0 {
		params.Limit = aws.Int64(limit)
	}

	if startID != "" {
		params.ExclusiveStartKey = map[string]*dynamodb.AttributeValue{
			"id": {S: aws.String(startID)},
		}
	}

	out, err := db.DB.Scan(params)
	if err != nil {
		return
	}

	clients = make([]osin.Client, aws.Int64Value(out.Count))

	for i, v := range out.Items {
		clients[i] = &Client{}
		err = dynamodbattribute.ConvertFromMap(v, clients[i])
		if err != nil {
			return
		}
	}

	return
}
