package storage

import (
	"context"

	"code.justin.tv/live/autohost/internal/hosting/models"

	"code.justin.tv/creator-collab/log"
	"code.justin.tv/live/autohost/lib"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface"
)

const (
	settingsAttrAutohostEnabled     = "AutohostEnabled"
	settingsAttrAutohostTeamHost    = "AutohostTeamHost"
	settingsAttrAutohostStrategy    = "AutohostStrategy"
	settingsAttrAutohostList        = "AutohostList"
	settingsAttrDeprioritizeVodcast = "AutohostDeprioritizeVodcast"
)

// Storage exposes a domain specific interface to specific persistence tools
type Storage interface {
	GetSettings(ctx context.Context, userID string) (*lib.Settings, error)
	UpdateSettings(ctx context.Context, userID string, settingsInput *lib.UpdateSettingsInput) (*lib.Settings, error)
	DeleteSettings(ctx context.Context, userID string) error

	GetList(ctx context.Context, userID string) ([]string, error)
	SetList(ctx context.Context, userID string, channelIDs []string) ([]string, error)

	GetAddedList(ctx context.Context, userID string) ([]lib.AddedChannel, error)
	AddToAddedList(ctx context.Context, targetUserID, sourceUserID string, score float64) error
	DeleteFromAddedList(ctx context.Context, targetUserID, hosterUserID string) error
	DeleteAddedList(ctx context.Context, userID string) error

	GetHost(ctx context.Context, sourceUserID string) (*models.DBHost, error)
	// DeleteHost removes the host item from DynamoDB. It is meant to clean up data when a user is deleted,
	// and should be used to unhost a channel.
	DeleteHost(ctx context.Context, sourceUserID string) error
	SetHost(ctx context.Context, sourceUserID, targetUserID string) (*models.DBHost, error)
}

type Params struct {
	Client            dynamodbiface.DynamoDBAPI
	TestParams        *TestParams
	Logger            log.Logger
	HostTableName     string
	SettingsTableName string
}

// New returns a new Dynamo struct
func New(p *Params) Storage {
	return &storageImpl{
		client:            p.Client,
		testParams:        p.TestParams,
		logger:            p.Logger,
		hostTableName:     p.HostTableName,
		settingsTableName: p.SettingsTableName,
	}
}

// Dynamo is a domain specific wrapper for the aws client
type storageImpl struct {
	client     dynamodbiface.DynamoDBAPI
	testParams *TestParams
	logger     log.Logger

	hostTableName     string
	settingsTableName string
}

// storageImpl implements the Storage interface.
var _ Storage = &storageImpl{}

// TestParams is used to set certain values for tests
type TestParams struct {
	Limit *int64
}

func ChannelKey(channelID string) map[string]*dynamodb.AttributeValue {
	return map[string]*dynamodb.AttributeValue{
		"ID": {
			S: &channelID,
		},
		"Resource": {
			S: aws.String("Channel"),
		},
	}
}

func settingsTableKey(channelID string) map[string]*dynamodb.AttributeValue {
	return map[string]*dynamodb.AttributeValue{
		"ChannelID": {
			S: aws.String(channelID),
		},
	}
}

// StringSet is []string type that implements the Marshaler interface from
// dynamodbattribute. Using this as an argument to expression.Value will create
// a string set with a single item from the string.
//
// See https://github.com/aws/aws-sdk-go/issues/1990
type StringSet []string

// MarshalDynamoDBAttributeValue implements the Marshaler interface
func (ss StringSet) MarshalDynamoDBAttributeValue(av *dynamodb.AttributeValue) error {
	av.SS = aws.StringSlice(ss)
	return nil
}
