package liveline

import (
	"context"
	"net/http"

	"code.justin.tv/foundation/twitchclient"
	"code.justin.tv/live/autohost/internal/metrics"
	"github.com/pkg/errors"

	telemetry "code.justin.tv/amzn/TwitchTelemetry"

	"code.justin.tv/discovery/liveline/proto/liveline"
	"github.com/cactus/go-statsd-client/statsd"
)

// Client is a higher level interface to the Liveline service that Autohost uses.
type Client interface {
	SortChannelsByCCV(ctx context.Context, channelIDs []string) ([]string, error)
	GetLiveChannelSet(ctx context.Context, channelIDs []string) (map[string]bool, error)
}

type livelineImpl struct {
	client liveline.Liveline
}

// livelineImpl implements the Liveline interface.
var _ Client = &livelineImpl{}

func NewClient(url string, statsClient statsd.Statter, sampleReporter *telemetry.SampleReporter) Client {
	httpClient := twitchclient.NewHTTPClient(twitchclient.ClientConf{
		TimingXactName: "liveline",
		Stats:          statsClient,
		RoundTripperWrappers: []func(http.RoundTripper) http.RoundTripper{
			metrics.NewTwirpClientMiddlewareWrapper(&metrics.TwirpClientMiddlewareWrapperConfig{
				SampleReporter: sampleReporter,
			}),
		},
	})
	livelineClient := liveline.NewLivelineProtobufClient(url, httpClient)

	return &livelineImpl{
		client: livelineClient,
	}
}

func (c *livelineImpl) SortChannelsByCCV(ctx context.Context, channelIDs []string) ([]string, error) {
	request := &liveline.StreamsByChannelIDsRequest{
		ChannelIds: channelIDs,
		SortKey:    liveline.SortField_CCV,
		Order:      liveline.SortOrder_DESC,
	}
	response, err := c.client.GetStreamsByChannelIDs(ctx, request)
	if err != nil {
		return nil, errors.Wrap(err, "Call to Liveline.StreamsByChannelIDsRequest failed")
	}

	streams := response.GetStreams()

	// Create list of live channels, sorted by CCV.
	liveChannelIDSet := make(map[string]bool, len(streams))
	sortedLiveChannelIDs := make([]string, 0, len(streams))

	for _, stream := range streams {
		channelID := stream.GetChannelId()
		if channelID == "" {
			continue
		} else if alreadyAdded := liveChannelIDSet[channelID]; alreadyAdded {
			continue
		}

		liveChannelIDSet[channelID] = true
		sortedLiveChannelIDs = append(sortedLiveChannelIDs, channelID)
	}

	// Append offline channels to the list.
	sortedChannelIDs := make([]string, 0, len(channelIDs))
	sortedChannelIDs = append(sortedChannelIDs, sortedLiveChannelIDs...)
	for _, channelID := range channelIDs {
		if !liveChannelIDSet[channelID] {
			sortedChannelIDs = append(sortedChannelIDs, channelID)
		}
	}

	return sortedChannelIDs, nil
}

func (c *livelineImpl) GetLiveChannelSet(ctx context.Context, channelIDs []string) (map[string]bool, error) {
	if len(channelIDs) == 0 {
		return make(map[string]bool, 0), nil
	}

	request := &liveline.StreamsByChannelIDsRequest{
		ChannelIds: channelIDs,
	}
	response, err := c.client.GetStreamsByChannelIDs(ctx, request)
	if err != nil {
		return nil, errors.Wrap(err, "Call to Liveline.StreamsByChannelIDsRequest failed")
	}

	streams := response.GetStreams()
	liveChannels := make(map[string]bool, len(streams))
	for _, stream := range streams {
		channelID := stream.GetChannelId()
		if channelID == "" {
			continue
		}
		liveChannels[channelID] = true
	}

	return liveChannels, nil
}
