package storage

import (
	"context"
	"time"

	"code.justin.tv/live/autohost/internal/hosting/models"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
	"github.com/aws/aws-sdk-go/service/dynamodb/expression"

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

func (db *storageImpl) GetHost(ctx context.Context, sourceUserID string) (*models.DBHost, error) {
	builder := expression.NewBuilder()
	builder = builder.WithProjection(expression.NamesList(
		expression.Name("ChannelID"),
		expression.Name("HostingTargetID")))
	exp, err := builder.Build()
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - get host - build expression failed", errors.Fields{
			logfield.HosterChannelID: sourceUserID,
		})
	}

	ctx, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()

	output, err := db.client.GetItemWithContext(ctx, &dynamodb.GetItemInput{
		ExpressionAttributeNames: exp.Names(),
		Key:                      HostTableKey(sourceUserID),
		ProjectionExpression:     exp.Projection(),
		TableName:                aws.String(db.hostTableName),
	})
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - get host - get item failed", errors.Fields{
			logfield.HosterChannelID: sourceUserID,
		})
	}
	if output.Item == nil {
		return &models.DBHost{
			ChannelID: sourceUserID,
		}, nil
	}

	var dbHost models.DBHost
	err = dynamodbattribute.UnmarshalMap(output.Item, &dbHost)
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - get host - unmarshal failed", errors.Fields{
			logfield.HosterChannelID: sourceUserID,
		})
	}

	return &dbHost, nil
}

func (db *storageImpl) DeleteHost(ctx context.Context, sourceUserID string) error {
	ctx, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()

	_, err := db.client.DeleteItemWithContext(ctx, &dynamodb.DeleteItemInput{
		Key:       HostTableKey(sourceUserID),
		TableName: aws.String(db.hostTableName),
	})
	if err != nil {
		return errors.Wrap(err, "dynamodb - deleting host by source id failed", errors.Fields{
			logfield.HosterChannelID: sourceUserID,
		})
	}

	return nil
}

func (db *storageImpl) SetHost(ctx context.Context, sourceUserID, targetUserID string) (*models.DBHost, error) {
	updateExpression := expression.Set(
		expression.Name("HostingTargetID"),
		expression.Value(targetUserID))
	builder := expression.NewBuilder().WithUpdate(updateExpression)

	exp, err := builder.Build()
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - set host - build expression failed", errors.Fields{
			logfield.HosterChannelID:     sourceUserID,
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	ctx, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()

	output, err := db.client.UpdateItemWithContext(ctx, &dynamodb.UpdateItemInput{
		ExpressionAttributeNames:  exp.Names(),
		ExpressionAttributeValues: exp.Values(),
		Key:                       HostTableKey(sourceUserID),
		ReturnValues:              aws.String(dynamodb.ReturnValueAllNew),
		TableName:                 aws.String(db.hostTableName),
		UpdateExpression:          exp.Update(),
	})
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - set host - update item failed", errors.Fields{
			logfield.HosterChannelID:     sourceUserID,
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	var dbHost models.DBHost
	err = dynamodbattribute.UnmarshalMap(output.Attributes, &dbHost)
	if err != nil {
		return nil, errors.Wrap(err, "dynamodb - set host - unmarshal failed", errors.Fields{
			logfield.HosterChannelID:     sourceUserID,
			logfield.HostTargetChannelID: targetUserID,
		})
	}

	return &dbHost, nil
}

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