package list

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/MakeNowJust/heredoc"
	"github.com/cli/cli/v2/internal/config"
	"github.com/cli/cli/v2/pkg/httpmock"
	"github.com/cli/cli/v2/pkg/iostreams"
)

func TestListRun(t *testing.T) {
	tests := []struct {
		name       string
		opts       ListOptions
		isTTY      bool
		wantStdout string
		wantStderr string
		wantErr    bool
	}{
		{
			name: "list authentication and signing keys; in tty",
			opts: ListOptions{
				HTTPClient: func() (*http.Client, error) {
					createdAt := time.Now().Add(time.Duration(-24) * time.Hour)
					reg := &httpmock.Registry{}
					reg.Register(
						httpmock.REST("GET", "user/keys"),
						httpmock.StringResponse(fmt.Sprintf(`[
							{
								"id": 1234,
								"key": "ssh-rsa AAAABbBB123",
								"title": "Mac",
								"created_at": "%[1]s"
							},
							{
								"id": 5678,
								"key": "ssh-rsa EEEEEEEK247",
								"title": "hubot@Windows",
								"created_at": "%[1]s"
							}
						]`, createdAt.Format(time.RFC3339))),
					)
					reg.Register(
						httpmock.REST("GET", "user/ssh_signing_keys"),
						httpmock.StringResponse(fmt.Sprintf(`[
							{
								"id": 321,
								"key": "ssh-rsa AAAABbBB123",
								"title": "Mac Signing",
								"created_at": "%[1]s"
							}
						]`, createdAt.Format(time.RFC3339))),
					)
					return &http.Client{Transport: reg}, nil
				},
			},
			isTTY: true,
			wantStdout: heredoc.Doc(`
				TITLE          ID    KEY                  TYPE            ADDED
				Mac            1234  ssh-rsa AAAABbBB123  authentication  about 1 day ago
				hubot@Windows  5678  ssh-rsa EEEEEEEK247  authentication  about 1 day ago
				Mac Signing    321   ssh-rsa AAAABbBB123  signing         about 1 day ago
			`),
			wantStderr: "",
		},
		{
			name: "list authentication and signing keys; in non-tty",
			opts: ListOptions{
				HTTPClient: func() (*http.Client, error) {
					createdAt, _ := time.Parse(time.RFC3339, "2020-08-31T15:44:24+02:00")
					reg := &httpmock.Registry{}
					reg.Register(
						httpmock.REST("GET", "user/keys"),
						httpmock.StringResponse(fmt.Sprintf(`[
							{
								"id": 1234,
								"key": "ssh-rsa AAAABbBB123",
								"title": "Mac",
								"created_at": "%[1]s"
							},
							{
								"id": 5678,
								"key": "ssh-rsa EEEEEEEK247",
								"title": "hubot@Windows",
								"created_at": "%[1]s"
							}
						]`, createdAt.Format(time.RFC3339))),
					)
					reg.Register(
						httpmock.REST("GET", "user/ssh_signing_keys"),
						httpmock.StringResponse(fmt.Sprintf(`[
							{
								"id": 321,
								"key": "ssh-rsa AAAABbBB123",
								"title": "Mac Signing",
								"created_at": "%[1]s"
							}
						]`, createdAt.Format(time.RFC3339))),
					)
					return &http.Client{Transport: reg}, nil
				},
			},
			isTTY: false,
			wantStdout: heredoc.Doc(`
				Mac	ssh-rsa AAAABbBB123	2020-08-31T15:44:24+02:00	1234	authentication
				hubot@Windows	ssh-rsa EEEEEEEK247	2020-08-31T15:44:24+02:00	5678	authentication
				Mac Signing	ssh-rsa AAAABbBB123	2020-08-31T15:44:24+02:00	321	signing
			`),
			wantStderr: "",
		},
		{
			name: "only authentication ssh keys are available",
			opts: ListOptions{
				HTTPClient: func() (*http.Client, error) {
					createdAt := time.Now().Add(time.Duration(-24) * time.Hour)
					reg := &httpmock.Registry{}
					reg.Register(
						httpmock.REST("GET", "user/keys"),
						httpmock.StringResponse(fmt.Sprintf(`[
							{
								"id": 1234,
								"key": "ssh-rsa AAAABbBB123",
								"title": "Mac",
								"created_at": "%[1]s"
							}
						]`, createdAt.Format(time.RFC3339))),
					)
					reg.Register(
						httpmock.REST("GET", "user/ssh_signing_keys"),
						httpmock.StatusStringResponse(404, "Not Found"),
					)
					return &http.Client{Transport: reg}, nil
				},
			},
			wantStdout: heredoc.Doc(`
				TITLE  ID    KEY                  TYPE            ADDED
				Mac    1234  ssh-rsa AAAABbBB123  authentication  about 1 day ago
			`),
			wantStderr: heredoc.Doc(`
			warning:  HTTP 404 (https://api.github.com/user/ssh_signing_keys?per_page=100)
			`),
			wantErr: false,
			isTTY:   true,
		},
		{
			name: "only signing ssh keys are available",
			opts: ListOptions{
				HTTPClient: func() (*http.Client, error) {
					createdAt := time.Now().Add(time.Duration(-24) * time.Hour)
					reg := &httpmock.Registry{}
					reg.Register(
						httpmock.REST("GET", "user/ssh_signing_keys"),
						httpmock.StringResponse(fmt.Sprintf(`[
							{
								"id": 1234,
								"key": "ssh-rsa AAAABbBB123",
								"title": "Mac",
								"created_at": "%[1]s"
							}
						]`, createdAt.Format(time.RFC3339))),
					)
					reg.Register(
						httpmock.REST("GET", "user/keys"),
						httpmock.StatusStringResponse(404, "Not Found"),
					)
					return &http.Client{Transport: reg}, nil
				},
			},
			wantStdout: heredoc.Doc(`
				TITLE  ID    KEY                  TYPE     ADDED
				Mac    1234  ssh-rsa AAAABbBB123  signing  about 1 day ago
			`),
			wantStderr: heredoc.Doc(`
				warning:  HTTP 404 (https://api.github.com/user/keys?per_page=100)
			`),
			wantErr: false,
			isTTY:   true,
		},
		{
			name: "no keys tty",
			opts: ListOptions{
				HTTPClient: func() (*http.Client, error) {
					reg := &httpmock.Registry{}
					reg.Register(
						httpmock.REST("GET", "user/keys"),
						httpmock.StringResponse(`[]`),
					)
					reg.Register(
						httpmock.REST("GET", "user/ssh_signing_keys"),
						httpmock.StringResponse(`[]`),
					)
					return &http.Client{Transport: reg}, nil
				},
			},
			wantStdout: "",
			wantStderr: "",
			wantErr:    true,
			isTTY:      true,
		},
		{
			name: "no keys non-tty",
			opts: ListOptions{
				HTTPClient: func() (*http.Client, error) {
					reg := &httpmock.Registry{}
					reg.Register(
						httpmock.REST("GET", "user/keys"),
						httpmock.StringResponse(`[]`),
					)
					reg.Register(
						httpmock.REST("GET", "user/ssh_signing_keys"),
						httpmock.StringResponse(`[]`),
					)
					return &http.Client{Transport: reg}, nil
				},
			},
			wantStdout: "",
			wantStderr: "",
			wantErr:    true,
			isTTY:      false,
		},
	}

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

			opts := tt.opts
			opts.IO = ios
			opts.Config = func() (config.Config, error) { return config.NewBlankConfig(), nil }

			err := listRun(&opts)
			if (err != nil) != tt.wantErr {
				t.Errorf("listRun() return error: %v", err)
				return
			}

			if stdout.String() != tt.wantStdout {
				t.Errorf("wants stdout %q, got %q", tt.wantStdout, stdout.String())
			}
			if stderr.String() != tt.wantStderr {
				t.Errorf("wants stderr %q, got %q", tt.wantStderr, stderr.String())
			}
		})
	}
}
