package storage

import (
	"context"
	"reflect"

	"code.justin.tv/creator-collab/log/errors"
	"code.justin.tv/live/autohost/internal/logfield"
	"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/dynamodbattribute"
	"github.com/aws/aws-sdk-go/service/dynamodb/expression"
)

// GetSettings gets autohost settings from DynamoDB
func (db *storageImpl) GetSettings(ctx context.Context, userID string) (*lib.Settings, error) {
	exp, err := expression.NewBuilder().WithProjection(expression.NamesList(
		expression.Name("AutohostEnabled"),
		expression.Name("AutohostTeamHost"),
		expression.Name("AutohostStrategy"),
		expression.Name("AutohostDeprioritizeVodcast"),
	)).Build()
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - getting settings - building expression failed", errors.Fields{
			logfield.UserID: userID,
		})
	}

	output, err := db.client.GetItemWithContext(ctx, &dynamodb.GetItemInput{
		Key:                      settingsTableKey(userID),
		TableName:                aws.String(db.settingsTableName),
		ProjectionExpression:     exp.Projection(),
		ExpressionAttributeNames: exp.Names(),
	})
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - getting settings - GetItem failed", errors.Fields{
			logfield.UserID: userID,
		})
	}

	return db.unmarshalSettings(output.Item)
}

// UpdateSettingsInRaidsTable updates autohost settings in DynamoDB
func (db *storageImpl) UpdateSettings(
	ctx context.Context, userID string, settingsInput *lib.UpdateSettingsInput) (*lib.Settings, error) {

	settingsMap := map[string]interface{}{
		settingsAttrAutohostEnabled:     settingsInput.Enabled,
		settingsAttrAutohostTeamHost:    settingsInput.TeamHost,
		settingsAttrAutohostStrategy:    settingsInput.Strategy,
		settingsAttrDeprioritizeVodcast: settingsInput.DeprioritizeVodcast,
	}

	setExp := expression.UpdateBuilder{}
	hasUpdates := false
	for name, value := range settingsMap {
		if !reflect.ValueOf(value).IsNil() {
			setExp = setExp.Set(expression.Name(name), expression.Value(value))
			hasUpdates = true
		}
	}
	if !hasUpdates {
		return db.GetSettings(ctx, userID)
	}

	exp, err := expression.NewBuilder().WithUpdate(setExp).Build()
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - update settings - building expression failed", errors.Fields{
			logfield.UserID: userID,
		})
	}

	output, err := db.client.UpdateItemWithContext(ctx, &dynamodb.UpdateItemInput{
		Key:                       settingsTableKey(userID),
		TableName:                 aws.String(db.settingsTableName),
		UpdateExpression:          exp.Update(),
		ExpressionAttributeNames:  exp.Names(),
		ExpressionAttributeValues: exp.Values(),
		ReturnValues:              aws.String(dynamodb.ReturnValueAllNew),
	})
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - update settings - UpdateItem failed", errors.Fields{
			logfield.UserID: userID,
		})
	}

	return db.unmarshalSettings(output.Attributes)
}

func (db *storageImpl) DeleteSettings(ctx context.Context, userID string) error {
	input := dynamodb.DeleteItemInput{
		Key:       settingsTableKey(userID),
		TableName: aws.String(db.settingsTableName),
	}
	_, err := db.client.DeleteItemWithContext(ctx, &input)
	if err != nil {
		return errors.Wrap(err, "dynamodb - delete settings - DeleteItemWithContext failed", errors.Fields{
			logfield.UserID: userID,
		})
	}

	return nil
}

func (db *storageImpl) unmarshalSettings(attributes map[string]*dynamodb.AttributeValue) (*lib.Settings, error) {
	if attributes == nil {
		return nil, nil
	}

	settings := &lib.Settings{}
	err := dynamodbattribute.UnmarshalMap(attributes, settings)
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - update settings - unmarshal failed")
	}

	return settings, nil
}
