package release

import (
	"context"
	"fmt"
	"testing"

	"github.com/google/go-github/v66/github"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func strPtr(s string) *string {
	return &s
}

type mockGithubReleaseService struct {
	release  *github.RepositoryRelease
	response *github.Response
	err      error
}

func (m *mockGithubReleaseService) GetLatestRelease(ctx context.Context, owner, repo string) (*github.RepositoryRelease, *github.Response, error) {
	return m.release, m.response, m.err
}

func TestGetLatestRelease(t *testing.T) {
	var (
		tests = []struct {
			name    string
			tagName string
			htmlURL string
			err     error
			want    *github.RepositoryRelease
		}{
			{
				name:    "success",
				tagName: "0.17.1",
				htmlURL: "https://github.com/flipt-io/flipt/releases/tag/0.17.2",
				err:     nil,
				want: &github.RepositoryRelease{
					TagName: strPtr("0.17.1"),
					HTMLURL: strPtr("https://github.com/flipt-io/flipt/releases/tag/0.17.2"),
				},
			},
			{
				name: "error",
				err:  fmt.Errorf("error getting release"),
			},
		}
	)

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {

			srv := &mockGithubReleaseService{
				release: &github.RepositoryRelease{
					TagName: strPtr(tt.tagName),
					HTMLURL: strPtr(tt.htmlURL),
				},
				err: tt.err,
			}

			rc := githubReleaseCheckerImpl{
				client: srv,
			}

			got, err := rc.getLatestRelease(context.Background())
			if tt.err != nil {
				assert.Equal(t, fmt.Sprintf("checking for latest version: %s", tt.err.Error()), err.Error())
				return
			}

			require.NoError(t, err)
			assert.Equal(t, tt.want, got)
		})
	}

}

type mockReleaseChecker struct {
	tagName string
	htmlURL string
	err     error
}

func (m *mockReleaseChecker) getLatestRelease(ctx context.Context) (*github.RepositoryRelease, error) {
	return &github.RepositoryRelease{
		TagName: &m.tagName,
		HTMLURL: &m.htmlURL,
	}, m.err
}

func TestCheck(t *testing.T) {
	var (
		tests = []struct {
			name    string
			version string
			tagName string
			htmlURL string
			want    Info
		}{
			{
				name:    "latest version",
				version: "0.17.1",
				tagName: "0.17.1",
				htmlURL: "",
				want: Info{
					CurrentVersion:   "0.17.1",
					LatestVersion:    "0.17.1",
					UpdateAvailable:  false,
					LatestVersionURL: "",
				},
			},
			{
				name:    "new version",
				version: "0.17.1",
				tagName: "0.17.2",
				htmlURL: "https://github.com/flipt-io/flipt/releases/tag/0.17.2",
				want: Info{
					CurrentVersion:   "0.17.1",
					LatestVersion:    "0.17.2",
					UpdateAvailable:  true,
					LatestVersionURL: "https://github.com/flipt-io/flipt/releases/tag/0.17.2",
				},
			},
		}
	)

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			rc := &mockReleaseChecker{
				tagName: tt.tagName,
				htmlURL: tt.htmlURL,
			}

			got, err := check(context.Background(), rc, tt.version)
			require.NoError(t, err)
			assert.Equal(t, tt.want, got)
		})
	}

}

func TestIs(t *testing.T) {
	var (
		tests = []struct {
			version string
			want    bool
		}{
			{
				version: "0.17.1",
				want:    true,
			},
			{
				version: "1.0.0",
				want:    true,
			},
			{
				version: "dev",
				want:    false,
			},
			{
				version: "1.0.0-snapshot",
				want:    false,
			},
			{
				version: "1.0.0-rc1",
				want:    false,
			},
			{
				version: "1.0.0-rc.1",
				want:    false,
			},
		}
	)

	for _, tt := range tests {
		tt := tt
		t.Run(tt.version, func(t *testing.T) {
			assert.Equal(t, tt.want, Is(tt.version))
		})
	}
}
