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"
)

const (
	validAccToken   = "acc_token"
	unauthAccToken  = "not_authorized_acc_token"
	validGuestToken = "valid_guest_token"
)

func TestGetGuestToken(t *testing.T) {
	redisMock := tester.NewRedisMock()
	tests := map[string]struct {
		accToken         string
		httpClientDoFunc func(req *http.Request) (*http.Response, error)
		loginFunc        func(r redis.Client) (string, error)
		expToken         string
		expErr           error
	}{
		"success": {
			accToken:         validAccToken,
			httpClientDoFunc: successGuestHttpDo,
			expToken:         validGuestToken,
		},
		"success_unauthorized": {
			accToken:         unauthAccToken,
			httpClientDoFunc: successGuestHttpDo,
			expToken:         validGuestToken,
			loginFunc:        validLoginFunc,
		},
		"err_http": {
			httpClientDoFunc: errorHttpDo,
			expErr:           someErr,
		},
		"unknown_http_error": {
			httpClientDoFunc: successGuestHttpDo,
			expErr:           errors.New("superset guest token answered with status: Internal server error"),
		},
		"login_failed": {
			accToken:         unauthAccToken,
			httpClientDoFunc: successGuestHttpDo,
			loginFunc:        invalidLoginFunc,
			expErr:           someErr,
		},
		"login_wrong_token": {
			accToken:         unauthAccToken,
			httpClientDoFunc: successGuestHttpDo,
			loginFunc:        wrongTokenLoginFunc,
			expErr:           errors.New("superset guest token answered with status: Internal server error"),
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			httpclient.Client = &mock.Client{DoFunc: tt.httpClientDoFunc}
			superset.SetLoginFunc(tt.loginFunc)

			got, err := superset.GetGuestToken(redisMock, tt.accToken)
			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.expToken, got)
		})
	}
}

func successGuestHttpDo(req *http.Request) (*http.Response, error) {
	accToken := req.Header.Get("Authorization")
	switch accToken {
	case "Bearer " + validAccToken:
		return &http.Response{
			StatusCode: http.StatusOK,
			Body:       ioutil.NopCloser(bytes.NewReader([]byte(`{"token":"valid_guest_token"}`))),
		}, nil
	case "Bearer " + unauthAccToken:
		return &http.Response{
			StatusCode: http.StatusUnauthorized,
			Body:       ioutil.NopCloser(bytes.NewReader([]byte(``))),
		}, nil
	default:
		return &http.Response{
			StatusCode: http.StatusInternalServerError,
			Status:     "Internal server error",
			Body:       ioutil.NopCloser(bytes.NewReader([]byte(``))),
		}, nil
	}
}

func validLoginFunc(r redis.Client) (string, error) {
	return validAccToken, nil
}

func invalidLoginFunc(r redis.Client) (string, error) {
	return "", someErr
}

func wrongTokenLoginFunc(r redis.Client) (string, error) {
	return "", nil
}
