package list

import (
	"net/http"
	"testing"

	"github.com/MakeNowJust/heredoc/v2"
	"github.com/stretchr/testify/assert"
	"gitlab.com/gitlab-org/cli/commands/cmdtest"
	"gitlab.com/gitlab-org/cli/pkg/httpmock"
	"gitlab.com/gitlab-org/cli/test"
)

func runCommand(rt http.RoundTripper, cli string) (*test.CmdOut, error) {
	ios, _, stdout, stderr := cmdtest.InitIOStreams(true, "")
	factory := cmdtest.InitFactory(ios, rt)

	// TODO: shouldn't be there but the stub doesn't work without it
	_, _ = factory.HttpClient()

	cmd := NewCmdList(factory, nil)

	return cmdtest.ExecuteCommand(cmd, cli, stdout, stderr)
}

var projectAccessTokenResponse = heredoc.Doc(`
	[
		{
		  "id": 10179584,
		  "user_id": 21973696,
		  "name": "sadfsdfsdf",
		  "scopes": [
		    "api",
		    "read_api"
		  ],
		  "created_at": "2024-07-07T07:59:35.767Z",
		  "last_used_at": null,
		  "expires_at": "2024-08-06",
		  "active": true,
		  "revoked": false,
		  "token": "",
		  "access_level": 10
		}
	]
`)

func TestListProjectAccessTokenAsText(t *testing.T) {
	fakeHTTP := &httpmock.Mocker{}
	defer fakeHTTP.Verify(t)

	fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/projects/OWNER/REPO/access_tokens",
		httpmock.NewStringResponse(http.StatusOK, projectAccessTokenResponse))
	output, err := runCommand(fakeHTTP, "")
	if err != nil {
		t.Errorf("error running command `token list`: %v", err)
	}

	out := output.String()

	assert.Equal(t, heredoc.Doc(`
		ID       NAME       ACCESS_LEVEL ACTIVE  REVOKED  CREATED_AT           EXPIRES_AT LAST_USED_AT SCOPES      
		10179584 sadfsdfsdf guest        true    false    2024-07-07T07:59:35Z 2024-08-06 -           api,read_api
	`), out)
	assert.Empty(t, output.Stderr())
}

func TestListProjectAccessTokenAsJSON(t *testing.T) {
	fakeHTTP := &httpmock.Mocker{}
	defer fakeHTTP.Verify(t)

	fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/projects/OWNER/REPO/access_tokens",
		httpmock.NewStringResponse(http.StatusOK, projectAccessTokenResponse))
	output, err := runCommand(fakeHTTP, "--output json")
	if err != nil {
		t.Errorf("error running command `token list --output json`: %v", err)
	}
	assert.Empty(t, output.Stderr())
	assert.JSONEq(t, projectAccessTokenResponse, output.String())
}

var groupAccessTokenResponse = heredoc.Doc(`
		[
		    {
		      "id": 10179685,
		      "user_id": 21973881,
		      "name": "sadfsdfsdf",
		      "scopes": [
		        "read_api"
		      ],
		      "created_at": "2024-07-07T08:41:16.287Z",
		      "expires_at": "2024-08-06",
		      "last_used_at": null,
		      "active": true,
		      "revoked": false,
		      "token": "",
		      "access_level": 10
		    }
		]
	`)

func TestListGroupAccessTokenAsText(t *testing.T) {
	fakeHTTP := &httpmock.Mocker{}
	defer fakeHTTP.Verify(t)

	fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/groups/GROUP/access_tokens",
		httpmock.NewStringResponse(http.StatusOK, groupAccessTokenResponse))
	output, err := runCommand(fakeHTTP, "--group GROUP")
	if err != nil {
		t.Errorf("error running command `token list --group GROUP`: %v", err)
	}

	out := output.String()

	assert.Equal(t, heredoc.Doc(`
		ID       NAME       ACCESS_LEVEL ACTIVE  REVOKED  CREATED_AT           EXPIRES_AT LAST_USED_AT SCOPES  
		10179685 sadfsdfsdf guest        true    false    2024-07-07T08:41:16Z 2024-08-06 -           read_api
	`), out)
	assert.Empty(t, output.Stderr())
}

func TestListGroupAccessTokenAsJSON(t *testing.T) {
	fakeHTTP := &httpmock.Mocker{}
	defer fakeHTTP.Verify(t)

	fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/groups/GROUP/access_tokens",
		httpmock.NewStringResponse(http.StatusOK, groupAccessTokenResponse))

	output, err := runCommand(fakeHTTP, "--group GROUP --output json")
	if err != nil {
		t.Errorf("error running command `token list --group GROUP --output json`: %v", err)
	}
	assert.Empty(t, output.Stderr())
	assert.JSONEq(t, groupAccessTokenResponse, output.String())
}

var personalAccessTokenResponse = heredoc.Doc(`
			[
			  {
			    "id": 9860015,
			    "name": "awsssm",
			    "revoked": false,
			    "created_at": "2024-05-29T07:25:56.846Z",
			    "scopes": [
			      "api"
			    ],
			    "user_id": 926857,
			    "active": false,
			    "expires_at": "2024-06-28"
			  },
			  {
			    "id": 9860076,
			    "name": "glab",
			    "revoked": false,
			    "created_at": "2024-05-29T07:34:14.044Z",
			    "scopes": [
			      "api"
			    ],
			    "user_id": 926857,
			    "last_used_at": "2024-06-05T17:32:34.466Z",
			    "active": false,
			    "expires_at": "2024-06-28"
			  },
			  {
			    "id": 10171440,
			    "name": "api",
			    "revoked": false,
			    "created_at": "2024-07-05T10:02:37.182Z",
			    "scopes": [
			      "api"
			    ],
			    "user_id": 926857,
			    "last_used_at": "2024-07-07T20:02:49.595Z",
			    "active": true,
			    "expires_at": "2024-08-04"
			  }
			]
		`)

var userResponse = heredoc.Doc(`
	{
	  "id": 1,
	  "username": "johndoe",
	  "name": "John Doe",
	  "state": "active",
	  "locked": false,
	  "avatar_url": "https://secure.gravatar.com/avatar/johndoe?s=80&d=identicon",
	  "web_url": "https://gitlab.com/johndoe",
	  "created_at": "2017-01-05T08:36:01.368Z",
	  "bio": "",
	  "location": "",
	  "public_email": "",
	  "skype": "",
	  "linkedin": "",
	  "twitter": "",
	  "discord": "",
	  "website_url": "",
	  "organization": "",
	  "job_title": "",
	  "pronouns": null,
	  "bot": false,
	  "work_information": null,
	  "local_time": null,
	  "last_sign_in_at": "2024-07-07T06:57:16.562Z",
	  "confirmed_at": "2017-01-05T08:36:24.701Z",
	  "last_activity_on": "2024-07-07",
	  "email": "john.doe@acme.com",
	  "theme_id": null,
	  "color_scheme_id": 1,
	  "projects_limit": 100000,
	  "current_sign_in_at": "2024-07-07T07:57:57.858Z",
	  "identities": [
	    {
	      "provider": "google_oauth2",
	      "extern_uid": "102139960402025821780",
	      "saml_provider_id": null
	    }
	  ],
	  "can_create_group": true,
	  "can_create_project": true,
	  "two_factor_enabled": true,
	  "external": false,
	  "private_profile": false,
	  "commit_email": "john.doe@acme.com",
	  "shared_runners_minutes_limit": 2000,
	  "extra_shared_runners_minutes_limit": null,
	  "scim_identities": []
	}
`)

func TestListPersonalAccessTokenAsText(t *testing.T) {
	fakeHTTP := &httpmock.Mocker{}
	defer fakeHTTP.Verify(t)

	fakeHTTP.RegisterResponder(http.MethodGet, "/personal_access_tokens",
		httpmock.NewStringResponse(http.StatusOK, personalAccessTokenResponse))
	fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/user",
		httpmock.NewStringResponse(http.StatusOK, userResponse))

	output, err := runCommand(fakeHTTP, "--user @me")
	if err != nil {
		t.Errorf("error running command `token list --user @me`: %v", err)
	}

	out := output.String()

	assert.Equal(t, heredoc.Doc(`
		ID       NAME   ACCESS_LEVEL ACTIVE  REVOKED  CREATED_AT           EXPIRES_AT LAST_USED_AT         SCOPES 
		9860015  awsssm -            false   false    2024-05-29T07:25:56Z 2024-06-28 -                    api    
		9860076  glab   -            false   false    2024-05-29T07:34:14Z 2024-06-28 2024-06-05T17:32:34Z api    
		10171440 api    -            true    false    2024-07-05T10:02:37Z 2024-08-04 2024-07-07T20:02:49Z api    
	`), out)
	assert.Empty(t, output.Stderr())
}

func TestListActivePersonalAccessTokenAsText(t *testing.T) {
	fakeHTTP := &httpmock.Mocker{}
	defer fakeHTTP.Verify(t)

	fakeHTTP.RegisterResponder(http.MethodGet, "/personal_access_tokens",
		httpmock.NewStringResponse(http.StatusOK, personalAccessTokenResponse))
	fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/user",
		httpmock.NewStringResponse(http.StatusOK, userResponse))

	output, err := runCommand(fakeHTTP, "--user @me --active")
	if err != nil {
		t.Errorf("error running command `token list --user @me`: %v", err)
	}

	out := output.String()

	assert.Equal(t, heredoc.Doc(`
		ID       NAME  ACCESS_LEVEL ACTIVE  REVOKED  CREATED_AT           EXPIRES_AT LAST_USED_AT         SCOPES 
		10171440 api   -            true    false    2024-07-05T10:02:37Z 2024-08-04 2024-07-07T20:02:49Z api    
	`), out)
	assert.Empty(t, output.Stderr())
}

func TestListPersonalAccessTokenAsJSON(t *testing.T) {
	fakeHTTP := &httpmock.Mocker{}
	defer fakeHTTP.Verify(t)

	fakeHTTP.RegisterResponder(http.MethodGet, "/personal_access_tokens",
		httpmock.NewStringResponse(http.StatusOK, personalAccessTokenResponse))
	fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/user",
		httpmock.NewStringResponse(http.StatusOK, userResponse))

	output, err := runCommand(fakeHTTP, "--user @me --output json")
	if err != nil {
		t.Errorf("error running command `token list --user @me`: %v", err)
	}

	assert.Empty(t, output.Stderr())
	assert.JSONEq(t, personalAccessTokenResponse, output.String())
}
