package storage

import (
	"context"

	"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/aws/awserr"
	"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"
)

const addedListAttribute = "AutohostAddedList"
const addedListLimit = 100

// GetAddedList returns the list of users who have added targetUserID to their AutoHost list
func (db *storageImpl) GetAddedList(ctx context.Context, targetUserID string) ([]lib.AddedChannel, error) {
	exp, err := expression.NewBuilder().WithProjection(expression.NamesList(
		expression.Name(addedListAttribute),
	)).Build()
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - get hosters on 'target -> [hoster]' list - build expression failed", errors.Fields{
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	output, err := db.client.GetItemWithContext(ctx, &dynamodb.GetItemInput{
		Key:                      ChannelKey(targetUserID),
		TableName:                aws.String("Channels"),
		ProjectionExpression:     exp.Projection(),
		ExpressionAttributeNames: exp.Names(),
	})
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - get hosters on 'target -> [hoster]' list - GetItem failed", errors.Fields{
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	var addedList []string
	err = dynamodbattribute.Unmarshal(output.Item[addedListAttribute], &addedList)
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - get hosters on 'target -> [hoster]' list - Unmarshal failed", errors.Fields{
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	addedListWithScores := []lib.AddedChannel{}
	for _, cID := range addedList {
		addedListWithScores = append(addedListWithScores, lib.AddedChannel{ID: cID})
	}

	return addedListWithScores, nil
}

// AddToAddedList adds hosterUserID to the list of channels who have added targetUserID to their AutoHost list
func (db *storageImpl) AddToAddedList(ctx context.Context, targetUserID, hosterUserID string, score float64) error {
	exp, err := expression.
		NewBuilder().
		WithUpdate(
			expression.Add(
				expression.Name(addedListAttribute),
				expression.Value(StringSet([]string{hosterUserID}))),
		).
		WithCondition(
			expression.Or(
				expression.AttributeNotExists(expression.Name(addedListAttribute)),
				expression.Size(expression.Name(addedListAttribute)).LessThan(expression.Value(addedListLimit)),
			),
		).
		Build()
	if err != nil {
		return errors.Wrap(err, "dynamodb - adding hoster to 'target -> [hoster]' list - build expression failed", errors.Fields{
			logfield.HosterChannelID:     hosterUserID,
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	_, err = db.client.UpdateItemWithContext(ctx, &dynamodb.UpdateItemInput{
		Key:                       ChannelKey(targetUserID),
		TableName:                 aws.String("Channels"),
		ConditionExpression:       exp.Condition(),
		UpdateExpression:          exp.Update(),
		ExpressionAttributeNames:  exp.Names(),
		ExpressionAttributeValues: exp.Values(),
	})

	if err != nil && !db.isErrCodeConditionalCheckFailedException(err) {
		return errors.Wrap(err, "dynamodb - adding hoster to 'target -> [hoster]' list failed", errors.Fields{
			logfield.HosterChannelID:     hosterUserID,
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	return nil
}

func (db *storageImpl) isErrCodeConditionalCheckFailedException(err error) bool {
	if err == nil {
		return false
	}
	awsErr, isAWSErr := err.(awserr.Error)
	return isAWSErr && awsErr.Code() == dynamodb.ErrCodeConditionalCheckFailedException
}

// DeleteFromAddedList removes hosterUserID from the list of channels who have added targetUserID to their AutoHost list
func (db *storageImpl) DeleteFromAddedList(ctx context.Context, targetUserID, hosterUserID string) error {
	exp, err := expression.
		NewBuilder().
		WithUpdate(
			expression.Delete(
				expression.Name(addedListAttribute),
				expression.Value(StringSet([]string{hosterUserID})),
			),
		).
		Build()
	if err != nil {
		return errors.Wrap(err, "dynamodb - deleting hoster from 'target -> [hoster]' list - build expression failed", errors.Fields{
			logfield.HosterChannelID:     hosterUserID,
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	_, err = db.client.UpdateItemWithContext(ctx, &dynamodb.UpdateItemInput{
		Key:                       ChannelKey(targetUserID),
		TableName:                 aws.String("Channels"),
		UpdateExpression:          exp.Update(),
		ExpressionAttributeNames:  exp.Names(),
		ExpressionAttributeValues: exp.Values(),
	})

	if err != nil {
		return errors.Wrap(err, "dynamodb - deleting hoster from 'target -> [hoster]' list failed", errors.Fields{
			logfield.HosterChannelID:     hosterUserID,
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	return nil
}

func (db *storageImpl) DeleteAddedList(ctx context.Context, userID string) error {
	exp, err := expression.
		NewBuilder().
		WithUpdate(
			expression.Remove(
				expression.Name(addedListAttribute),
			),
		).
		Build()
	if err != nil {
		return errors.Wrap(err, "dynamodb - deleting added list - build expression failed", errors.Fields{
			logfield.UserID: userID,
		})
	}

	_, err = db.client.UpdateItemWithContext(ctx, &dynamodb.UpdateItemInput{
		Key:                       ChannelKey(userID),
		TableName:                 aws.String("Channels"),
		UpdateExpression:          exp.Update(),
		ExpressionAttributeNames:  exp.Names(),
		ExpressionAttributeValues: exp.Values(),
	})
	if err != nil {
		return errors.Wrap(err, "dynamodb - deleting added list - build expression failed", errors.Fields{
			logfield.UserID: userID,
		})
	}

	return nil
}
