package registration

import (
	"context"
	"sort"

	"code.justin.tv/sse/malachai/pkg/internal/opaquekey"

	"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/go-redis/redis"
)

// GetServicePageInput contains the parameters for retrieving a page of services
type GetServicePageInput struct {
	// number of services to return
	First int64

	// offset
	After string
}

func unmarshalOffset(after string) (serviceID string, serviceName string, err error) {
	keys, err := opaquekey.UnmarshalBase64Key(after, 2)
	if err != nil {
		return "", "", err
	}
	return keys[0], keys[1], nil
}

// GetServicePageOutput contains the return of GetServicePage
type GetServicePageOutput struct {
	Services []*Service
	// offset
	Offset string
}

func marshalOffset(services []*Service) (offset string) {
	last := services[len(services)-1]
	return opaquekey.MarshalBase64Key(last.ID, last.Name)
}

const (
	servicePaginationKey = "sorted_service_names"
)

// GetServicePageWithContext retrieves a page of services
func (reg *registrar) GetServicePageWithContext(ctx context.Context, input *GetServicePageInput) (output *GetServicePageOutput, err error) {
	var name string
	if input.After != "" {
		_, name, err = unmarshalOffset(input.After)
		if err != nil {
			return
		}
	}

	offset, err := reg.redis.ZRank(servicePaginationKey, name).Result()
	if err != nil {
		if err == redis.Nil {
			offset = 0
			err = nil
		} else {
			return
		}
	}

	if offset != 0 {
		offset++
	}

	serviceNames, err := reg.redis.ZRangeByLex(servicePaginationKey, redis.ZRangeBy{
		Min:    "-",
		Max:    "+",
		Offset: offset,
		Count:  input.First,
	}).Result()
	if err != nil {
		return
	}

	keys := make([]map[string]*dynamodb.AttributeValue, len(serviceNames))
	for i, serviceName := range serviceNames {
		keys[i] = map[string]*dynamodb.AttributeValue{serviceNameKey: {S: aws.String(serviceName)}}
	}

	params := &dynamodb.BatchGetItemInput{
		RequestItems: map[string]*dynamodb.KeysAndAttributes{
			reg.cfg.TableName: {
				Keys: keys,
			},
		},
	}

	out, err := reg.db.BatchGetItemWithContext(ctx, params)
	if err != nil {
		return
	}

	services := make([]*Service, 0, len(out.Responses[reg.cfg.TableName]))
	for _, item := range out.Responses[reg.cfg.TableName] {
		service := new(Service)
		err = dynamodbattribute.UnmarshalMap(item, service)
		if err != nil {
			return
		}
		services = append(services, service)
	}

	// sort the list of services returned from dynamodb
	sort.Sort(Sorter{
		Services: services,
		By:       ByNameCaseInsensitive,
	})

	output = &GetServicePageOutput{
		Services: services,
		Offset:   marshalOffset(services),
	}
	return
}
