package root

import (
	"fmt"
	"testing"

	"github.com/cli/cli/v2/internal/ghrepo"
	"github.com/cli/cli/v2/internal/prompter"
	"github.com/cli/cli/v2/pkg/extensions"
	"github.com/cli/cli/v2/pkg/iostreams"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestOfficialExtensionStubRun(t *testing.T) {
	ext := &extensions.OfficialExtension{Name: "cool", Owner: "github", Repo: "gh-cool"}

	tests := []struct {
		name          string
		isTTY         bool
		confirmResult bool
		confirmErr    error
		installErr    error
		wantErr       string
		wantStderr    string
		wantInstalled bool
	}{
		{
			name:       "non-TTY prints install instructions",
			isTTY:      false,
			wantStderr: "gh extension install github/gh-cool",
		},
		{
			name:          "TTY confirmed installs",
			isTTY:         true,
			confirmResult: true,
			wantStderr:    "Successfully installed github/gh-cool",
			wantInstalled: true,
		},
		{
			name:          "TTY declined does not install",
			isTTY:         true,
			confirmResult: false,
		},
		{
			name:       "TTY prompt error is propagated",
			isTTY:      true,
			confirmErr: fmt.Errorf("prompt interrupted"),
			wantErr:    "prompt interrupted",
		},
		{
			name:          "TTY install error is propagated",
			isTTY:         true,
			confirmResult: true,
			installErr:    fmt.Errorf("network error"),
			wantErr:       "network error",
			wantInstalled: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ios, _, _, stderr := iostreams.Test()
			if tt.isTTY {
				ios.SetStdinTTY(true)
				ios.SetStdoutTTY(true)
				ios.SetStderrTTY(true)
			}

			em := &extensions.ExtensionManagerMock{
				InstallFunc: func(_ ghrepo.Interface, _ string) error {
					return tt.installErr
				},
			}
			p := &prompter.PrompterMock{
				ConfirmFunc: func(_ string, _ bool) (bool, error) {
					return tt.confirmResult, tt.confirmErr
				},
			}

			err := officialExtensionStubRun(ios, p, em, ext)

			if tt.wantErr != "" {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.wantErr)
			} else {
				require.NoError(t, err)
			}

			if tt.wantStderr != "" {
				assert.Contains(t, stderr.String(), tt.wantStderr)
			}

			if tt.wantInstalled {
				require.NotEmpty(t, em.InstallCalls())
				repo := em.InstallCalls()[0].InterfaceMoqParam
				assert.Equal(t, "github", repo.RepoOwner())
				assert.Equal(t, "gh-cool", repo.RepoName())
				assert.Equal(t, "github.com", repo.RepoHost())
			} else if tt.isTTY && !tt.confirmResult && tt.confirmErr == nil {
				assert.Empty(t, em.InstallCalls())
			}
		})
	}
}

func TestNewCmdOfficialExtensionStub_Properties(t *testing.T) {
	ios, _, _, _ := iostreams.Test()
	ext := &extensions.OfficialExtension{Name: "cool", Owner: "github", Repo: "gh-cool"}
	em := &extensions.ExtensionManagerMock{}
	p := &prompter.PrompterMock{}

	cmd := NewCmdOfficialExtensionStub(ios, p, em, ext)

	assert.Equal(t, "cool", cmd.Use)
	assert.True(t, cmd.Hidden)
	assert.Equal(t, "extension", cmd.GroupID)
	assert.True(t, cmd.DisableFlagParsing)
}
