package shared

import (
	"bytes"
	"encoding/json"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/MakeNowJust/heredoc"
	"github.com/cli/cli/v2/api"
	"github.com/cli/cli/v2/internal/ghrepo"
	"github.com/cli/cli/v2/pkg/httpmock"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPreciseAgo(t *testing.T) {
	const form = "2006-Jan-02 15:04:05"
	now, _ := time.Parse(form, "2021-Apr-12 14:00:00")

	cases := map[string]string{
		"2021-Apr-12 14:00:00": "0s ago",
		"2021-Apr-12 13:59:30": "30s ago",
		"2021-Apr-12 13:59:00": "1m0s ago",
		"2021-Apr-12 13:30:15": "29m45s ago",
		"2021-Apr-12 13:00:00": "1h0m0s ago",
		"2021-Apr-12 02:30:45": "11h29m15s ago",
		"2021-Apr-11 14:00:00": "24h0m0s ago",
		"2021-Apr-01 14:00:00": "264h0m0s ago",
		"2021-Mar-12 14:00:00": "Mar 12, 2021",
	}

	for createdAt, expected := range cases {
		d, _ := time.Parse(form, createdAt)
		got := preciseAgo(now, d)
		if got != expected {
			t.Errorf("expected %s but got %s for %s", expected, got, createdAt)
		}
	}
}

func TestGetAnnotations404(t *testing.T) {
	reg := &httpmock.Registry{}
	defer reg.Verify(t)

	reg.Register(
		httpmock.REST("GET", "repos/OWNER/REPO/check-runs/123456/annotations"),
		httpmock.StatusStringResponse(404, "not found"))

	httpClient := &http.Client{Transport: reg}
	apiClient := api.NewClientFromHTTP(httpClient)
	repo := ghrepo.New("OWNER", "REPO")

	result, err := GetAnnotations(apiClient, repo, Job{ID: 123456, Name: "a job"})
	assert.NoError(t, err)
	assert.Equal(t, result, []Annotation{})
}

func TestRun_Duration(t *testing.T) {
	now, _ := time.Parse(time.RFC3339, "2022-07-20T11:22:58Z")

	tests := []struct {
		name  string
		json  string
		wants string
	}{
		{
			name: "no run_started_at",
			json: heredoc.Doc(`
				{
					"created_at": "2022-07-20T11:20:13Z",
					"updated_at": "2022-07-20T11:21:16Z",
					"status": "completed"
				}`),
			wants: "1m3s",
		},
		{
			name: "with run_started_at",
			json: heredoc.Doc(`
				{
					"created_at": "2022-07-20T11:20:13Z",
					"run_started_at": "2022-07-20T11:20:55Z",
					"updated_at": "2022-07-20T11:21:16Z",
					"status": "completed"
				}`),
			wants: "21s",
		},
		{
			name: "in_progress",
			json: heredoc.Doc(`
				{
					"created_at": "2022-07-20T11:20:13Z",
					"run_started_at": "2022-07-20T11:20:55Z",
					"updated_at": "2022-07-20T11:21:16Z",
					"status": "in_progress"
				}`),
			wants: "2m3s",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var r Run
			assert.NoError(t, json.Unmarshal([]byte(tt.json), &r))
			assert.Equal(t, tt.wants, r.Duration(now).String())
		})
	}
}

func TestRunExportData(t *testing.T) {
	oldestStartedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:20:13Z")
	oldestCompletedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:21:16Z")
	newestStartedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:20:55Z")
	newestCompletedAt, _ := time.Parse(time.RFC3339, "2022-07-20T11:23:16Z")

	tests := []struct {
		name   string
		fields []string
		run    Run
		output string
	}{
		{
			name:   "exports workflow run's single job",
			fields: []string{"jobs"},
			run: Run{
				Jobs: []Job{
					{
						ID:         123456,
						Status:     "completed",
						Conclusion: "success",
						Name:       "macos",
						Steps: []Step{
							{
								Name:       "Checkout",
								Status:     "completed",
								Conclusion: "success",
								Number:     1,
							},
						},
						StartedAt:   oldestStartedAt,
						CompletedAt: oldestCompletedAt,
						URL:         "https://example.com/OWNER/REPO/actions/runs/123456",
					},
				},
			},
			output: `{"jobs":[{"completedAt":"2022-07-20T11:21:16Z","conclusion":"success","databaseId":123456,"name":"macos","startedAt":"2022-07-20T11:20:13Z","status":"completed","steps":[{"conclusion":"success","name":"Checkout","number":1,"status":"completed"}],"url":"https://example.com/OWNER/REPO/actions/runs/123456"}]}`,
		},
		{
			name:   "exports workflow run's multiple jobs",
			fields: []string{"jobs"},
			run: Run{
				Jobs: []Job{
					{
						ID:         123456,
						Status:     "completed",
						Conclusion: "success",
						Name:       "macos",
						Steps: []Step{
							{
								Name:       "Checkout",
								Status:     "completed",
								Conclusion: "success",
								Number:     1,
							},
						},
						StartedAt:   oldestStartedAt,
						CompletedAt: oldestCompletedAt,
						URL:         "https://example.com/OWNER/REPO/actions/runs/123456",
					},
					{
						ID:         234567,
						Status:     "completed",
						Conclusion: "error",
						Name:       "windows",
						Steps: []Step{
							{
								Name:       "Checkout",
								Status:     "completed",
								Conclusion: "error",
								Number:     2,
							},
						},
						StartedAt:   newestStartedAt,
						CompletedAt: newestCompletedAt,
						URL:         "https://example.com/OWNER/REPO/actions/runs/234567",
					},
				},
			},
			output: `{"jobs":[{"completedAt":"2022-07-20T11:21:16Z","conclusion":"success","databaseId":123456,"name":"macos","startedAt":"2022-07-20T11:20:13Z","status":"completed","steps":[{"conclusion":"success","name":"Checkout","number":1,"status":"completed"}],"url":"https://example.com/OWNER/REPO/actions/runs/123456"},{"completedAt":"2022-07-20T11:23:16Z","conclusion":"error","databaseId":234567,"name":"windows","startedAt":"2022-07-20T11:20:55Z","status":"completed","steps":[{"conclusion":"error","name":"Checkout","number":2,"status":"completed"}],"url":"https://example.com/OWNER/REPO/actions/runs/234567"}]}`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			exported := tt.run.ExportData(tt.fields)
			buf := bytes.Buffer{}
			enc := json.NewEncoder(&buf)
			require.NoError(t, enc.Encode(exported))
			assert.Equal(t, tt.output, strings.TrimSpace(buf.String()))
		})
	}
}
