package manager

import (
	"fmt"
	"net/http"

	"code.justin.tv/common/ddbmetrics"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dynamodb"
)

// NoSuchSecretError represents there being no secret with that name
type NoSuchSecretError struct {
	secret string
}

func (e NoSuchSecretError) Error() string {
	return fmt.Sprintf("no such secret: %s", e.secret)
}

// Code is the http status code that should be returned in the api response
func (e NoSuchSecretError) Code() int { return http.StatusNotFound }

// Get will fetch and decrypt a secret, returning a pointer to a
// Secret struct.
func (m *Manager) Get(secretName string) (*Secret, error) {

	secret, err := m.get(secretName)
	if err != nil {
		return nil, err
	}
	err = m.Decrypt(secret)
	if err != nil {
		return nil, err
	}
	return secret, nil
}

// GetEncrypted returns a pointer to a Secret, but doees not actually
// perform the decryption. This is used by the JSON API.
func (m *Manager) GetEncrypted(secretName string) (*Secret, error) {
	secret, err := m.get(secretName)
	return secret, err
}

func (m *Manager) get(name string) (*Secret, error) {
	queryInput := m.inputForGet(name)
	queryOutput, err := m.DynamoDB.Query(queryInput)
	if err != nil {
		return nil, err
	}
	m.metrics.Report(ddbmetrics.Read, queryOutput.ConsumedCapacity)
	if *queryOutput.Count == 0 {
		return nil, NoSuchSecretError{secret: name}
	}
	secret, err := unmarshalSecret(queryOutput.Items[0])
	if err != nil {
		return nil, err
	}

	return secret, nil
}

func (m *Manager) inputForGet(name string) *dynamodb.QueryInput {
	return &dynamodb.QueryInput{
		ConsistentRead: aws.Bool(true),
		ExpressionAttributeNames: map[string]*string{
			"#N": aws.String("name"),
		},
		ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
			":name": {
				S: aws.String(name),
			},
		},
		KeyConditionExpression: aws.String("#N = :name"),
		ReturnConsumedCapacity: aws.String("INDEXES"),
		TableName:              aws.String(m.TableName()),
	}
}
