package recs

import (
	"context"
	"net/http"
	"net/url"

	middleware "code.justin.tv/amzn/TwitchTelemetryMetricsMiddleware"
	"code.justin.tv/video/metrics-middleware/v2/operation"

	telemetry "code.justin.tv/amzn/TwitchTelemetry"
	"code.justin.tv/foundation/twitchclient"
	"code.justin.tv/live/autohost/internal/metrics"
	"github.com/cactus/go-statsd-client/statsd"

	"code.justin.tv/live/autohost/rpc/hosting"
	uuid "github.com/satori/go.uuid"

	twitchrecs "code.justin.tv/amzn/TwitchRecsTwirp"
	"code.justin.tv/amzn/TwitchS2S2/c7s"
	"code.justin.tv/amzn/TwitchS2S2/s2s2"
	"code.justin.tv/creator-collab/log/errors"
	"github.com/golang/protobuf/ptypes/wrappers"
)

type RecommendationItemType string

const (
	Category RecommendationItemType = "category"
	Stream                          = "stream"
	Clip                            = "clip"
	Video                           = "video"
)

type Client interface {
	GetProductID() string
	GetRecommendedChannels(ctx context.Context, userContext *hosting.UserContext, channelFilter []string, limit int64) ([]*twitchrecs.RecommendedItem, string, error)
}

type clientImpl struct {
	productID  string
	recsClient twitchrecs.TwitchRecs
}

func NewClient(s2s2ServiceName string, recsEndpointURL string, productID string, proxyEndpointURL string, statsClient statsd.Statter, sampleReporter *telemetry.SampleReporter) (Client, error) {
	opStarter := &operation.Starter{
		OpMonitors: []operation.OpMonitor{
			&middleware.OperationMonitor{
				SampleReporter: *sampleReporter,
			},
		},
	}

	s2s2Client, err := s2s2.New(&s2s2.Options{
		Config: &c7s.Config{
			ClientServiceName: s2s2ServiceName,
		},
		OperationStarter: opStarter,
	})

	if err != nil {
		return nil, errors.Wrap(err, "Failed to create recs clients because s2s2 failed to initialize")
	}

	var httpClient *http.Client

	// Proxy logic will be removed/updated once TeleportRemote is integrated with Autohost
	if proxyEndpointURL != "" {
		proxyURL, err := url.Parse(proxyEndpointURL)
		if err != nil {
			return nil, errors.Wrap(err, "Failed to parse proxy endpoint url.")
		}

		httpClient = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}}
	} else {
		httpClient = twitchclient.NewHTTPClient(twitchclient.ClientConf{
			TimingXactName: "twitchRecs",
			Stats:          statsClient,
			RoundTripperWrappers: []func(http.RoundTripper) http.RoundTripper{
				metrics.NewTwirpClientMiddlewareWrapper(&metrics.TwirpClientMiddlewareWrapperConfig{
					SampleReporter: sampleReporter,
				}),
			},
		})
	}

	s2s2AuthenticatedClient := &http.Client{Transport: s2s2Client.RoundTripper(httpClient)}
	recsClient := twitchrecs.NewTwitchRecsProtobufClient(recsEndpointURL, s2s2AuthenticatedClient)

	return &clientImpl{
		productID:  productID,
		recsClient: recsClient,
	}, nil
}

func (c *clientImpl) GetProductID() string {
	return c.productID
}

func (c *clientImpl) GetRecommendedChannels(ctx context.Context, userContext *hosting.UserContext, channelFilter []string, limit int64) ([]*twitchrecs.RecommendedItem, string, error) {
	requestID := uuid.NewV4().String()
	request := &twitchrecs.RecommendedStreamsRequest{
		RequestContext: &twitchrecs.RequestContext{
			RequestId: &wrappers.StringValue{
				Value: requestID,
			},
			ProductId: c.productID,
			Limit:     limit,
		},
		UserContext: &twitchrecs.UserContext{
			UserId:    userContext.GetUserId(),
			DeviceId:  userContext.GetDeviceId(),
			Country:   userContext.GetCountry(),
			Platform:  userContext.GetPlatform(),
			Languages: userContext.GetLanguages(),
		},
		Filters: &twitchrecs.Filters{
			RestrictionList: channelFilter,
		},
	}

	response, err := c.recsClient.GetRecommendedStreams(ctx, request)
	if err != nil {
		return nil, "", errors.Wrap(err, "Call to TwitchRecs.GetRecommendedStreams failed")
	}

	return response.GetItems(), requestID, nil
}
