package agent

import (
	"testing"

	"github.com/cli/cli/v2/internal/config"
	"github.com/cli/cli/v2/internal/gh"
	ghmock "github.com/cli/cli/v2/internal/gh/mock"
	"github.com/cli/cli/v2/pkg/cmdutil"
	"github.com/cli/cli/v2/pkg/iostreams"
	"github.com/stretchr/testify/require"
)

// setupMockOAuthConfig configures a blank config with a default host and optional token behavior.
func setupMockOAuthConfig(t *testing.T, tokenSource string) gh.Config {
	t.Helper()
	c := config.NewBlankConfig()
	switch tokenSource {
	case "oauth_token":
		// valid OAuth device flow token stored in config
		c.Set("github.com", "oauth_token", "gho_OAUTH123")
	case "keyring":
		// valid OAuth device flow token stored in keyring
		c.Set("github.com", "oauth_token", "gho_OAUTH123")
	case "GH_TOKEN":
		// classic style token stored in config (will fail prefix check)
		c.Set("github.com", "oauth_token", "ghp_CLASSIC123")
	case "GH_ENTERPRISE_TOKEN":
		// enterprise style token stored in config (will fail prefix check)
		c.Set("something.ghes.com", "oauth_token", "ghe_ENTERPRISE123")
	}
	return c
}

func TestNewCmdAgentTask(t *testing.T) {
	tests := []struct {
		name            string
		tokenSource     string
		customConfig    func() (gh.Config, error)
		wantErr         bool
		wantErrContains string
		wantStdout      string
	}{
		{
			name:        "oauth token is accepted",
			tokenSource: "oauth_token",
			wantErr:     false,
			wantStdout:  "",
		},
		{
			name:        "keyring oauth token is accepted",
			tokenSource: "keyring",
			wantErr:     false,
			wantStdout:  "",
		},
		{
			name:            "env var token is rejected",
			tokenSource:     "GH_TOKEN",
			wantErr:         true,
			wantErrContains: "requires an OAuth token",
		},
		{
			name:        "enterprise token alone is ignored and rejected",
			tokenSource: "GH_ENTERPRISE_TOKEN",
			wantErr:     true,
		},
		{
			name: "github.com oauth is accepted and enterprise token ignored",
			customConfig: func() (gh.Config, error) {
				c := config.NewBlankConfig()
				c.Set("something.ghes.com", "oauth_token", "ghe_ENTERPRISE123")
				c.Set("github.com", "oauth_token", "gho_OAUTH123")
				return c, nil
			},
			wantErr:    false,
			wantStdout: "",
		},
		{
			name: "enterprise host is rejected",
			customConfig: func() (gh.Config, error) {
				return &ghmock.ConfigMock{
					AuthenticationFunc: func() gh.AuthConfig {
						c := &config.AuthConfig{}
						c.SetDefaultHost("something.ghes.com", "GH_HOST")
						return c
					},
				}, nil
			},
			wantErr:         true,
			wantErrContains: "not supported on this host",
		},
		{
			name: "empty host is rejected",
			customConfig: func() (gh.Config, error) {
				return &ghmock.ConfigMock{
					AuthenticationFunc: func() gh.AuthConfig {
						c := &config.AuthConfig{}
						c.SetDefaultHost("", "GH_HOST")
						return c
					},
				}, nil
			},
			wantErr:         true,
			wantErrContains: "no default host configured",
		},
		{
			name:        "no auth is rejected",
			tokenSource: "",
			wantErr:     true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f := &cmdutil.Factory{}
			ios, _, stdout, _ := iostreams.Test()
			f.IOStreams = ios
			if tt.customConfig != nil {
				f.Config = tt.customConfig
			} else {
				f.Config = func() (gh.Config, error) { return setupMockOAuthConfig(t, tt.tokenSource), nil }
			}

			cmd := NewCmdAgentTask(f)
			err := cmd.Execute()

			if tt.wantErr {
				require.Error(t, err)
				if tt.wantErrContains != "" {
					require.Contains(t, err.Error(), tt.wantErrContains)
				}
			} else {
				require.NoError(t, err)
				require.Equal(t, tt.wantStdout, stdout.String())
			}
		})
	}
}

func TestAliasAreSet(t *testing.T) {
	f := &cmdutil.Factory{}
	ios, _, _, _ := iostreams.Test()
	f.IOStreams = ios
	f.Config = func() (gh.Config, error) { return setupMockOAuthConfig(t, "oauth_token"), nil }

	cmd := NewCmdAgentTask(f)

	require.ElementsMatch(t, []string{"agent-tasks", "agent", "agents"}, cmd.Aliases)
}
