package superset_test

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"testing"

	"github.com/fiskerinc/cloud-services/pkg/httpclient"
	"github.com/fiskerinc/cloud-services/pkg/httpclient/mock"
	"github.com/fiskerinc/cloud-services/pkg/redis"
	"github.com/fiskerinc/cloud-services/pkg/redis/tester"
	"github.com/fiskerinc/cloud-services/pkg/superset"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
)

var someErr = errors.New("some err")

func TestGetAccessToken(t *testing.T) {
	const validToken = "valid_token"

	redisMock := tester.NewRedisMock()
	type test struct {
		redisGetResults  interface{}
		httpClientDoFunc func(req *http.Request) (*http.Response, error)
		redisErr         error
		expErr           error
		expResp          string
		expSetValues     map[string]tester.ExpiringCache
	}

	tests := map[string]test{
		"success_cached": {
			redisGetResults: []byte(validToken),
			expErr:          nil,
			expResp:         validToken,
			expSetValues:    map[string]tester.ExpiringCache{},
		},
		"success": {
			httpClientDoFunc: successAccHttpDo,
			expErr:           nil,
			expResp:          validToken,
			expSetValues: map[string]tester.ExpiringCache{
				redis.SupersetAccTokenKey: {
					Value: validToken,
				},
			},
		},
		"failed_cache_func": {
			redisErr: someErr,
			expErr:   someErr,
		},
		"invalid_token": {
			redisGetResults: 12,
			expErr:          errors.New("invalid superset access token type (expected []byte]); access token: 12"),
		},
		"error_request": {
			httpClientDoFunc: errorHttpDo,
			expErr:           someErr,
		},
		"forbidden_request": {
			httpClientDoFunc: forbiddenAccHttpDo,
			expErr:           errors.New("superset login answered with status: 403 Forbidden"),
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			httpclient.Client = &mock.Client{DoFunc: tt.httpClientDoFunc}
			redisMock.Reset()
			redisMock.GetResults = tt.redisGetResults
			redisMock.Error = tt.redisErr
			got, err := superset.GetAccessToken(redisMock)
			if err != nil && tt.expErr != nil {
				assert.Equal(t, tt.expErr.Error(), err.Error())
				return
			}

			assert.Equal(t, tt.expErr, err)
			assert.Equal(t, tt.expResp, got)
			assert.Equal(t, tt.expSetValues, redisMock.SetValues)
		})
	}
}

func successAccHttpDo(req *http.Request) (*http.Response, error) {
	return &http.Response{
		StatusCode: http.StatusOK,
		Body:       ioutil.NopCloser(bytes.NewReader([]byte(`{"access_token":"valid_token","refresh_token":""}`))),
	}, nil
}

func errorHttpDo(req *http.Request) (*http.Response, error) {
	return nil, someErr
}

func forbiddenAccHttpDo(req *http.Request) (*http.Response, error) {
	return &http.Response{
		StatusCode: http.StatusForbidden,
		Status:     "403 Forbidden",
		Body:       ioutil.NopCloser(bytes.NewReader([]byte(``))),
	}, nil
}
