package git_commands

import (
	"testing"

	"github.com/jesseduffield/lazygit/pkg/commands/hosting_service"
	"github.com/jesseduffield/lazygit/pkg/commands/models"
	"github.com/stretchr/testify/assert"
)

func TestGetRepoInfoFromURL(t *testing.T) {
	cases := []struct {
		name     string
		url      string
		expected hosting_service.RepoInformation
	}{
		{
			name: "SSH URL",
			url:  "git@github.com:jesseduffield/lazygit.git",
			expected: hosting_service.RepoInformation{
				Owner:      "jesseduffield",
				Repository: "lazygit",
			},
		},
		{
			name: "HTTPS URL",
			url:  "https://github.com/jesseduffield/lazygit.git",
			expected: hosting_service.RepoInformation{
				Owner:      "jesseduffield",
				Repository: "lazygit",
			},
		},
		{
			name: "HTTPS URL without .git",
			url:  "https://github.com/jesseduffield/lazygit",
			expected: hosting_service.RepoInformation{
				Owner:      "jesseduffield",
				Repository: "lazygit",
			},
		},
		{
			name: "SSH URL with org nesting",
			url:  "git@github.com:my-org/sub-group/lazygit.git",
			expected: hosting_service.RepoInformation{
				Owner:      "my-org/sub-group",
				Repository: "lazygit",
			},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			result, err := hosting_service.GetRepoInfoFromURL(c.url)
			assert.NoError(t, err)
			assert.Equal(t, c.expected, result)
		})
	}
}

func TestGraphQLEndpoint(t *testing.T) {
	cases := []struct {
		host     string
		expected string
	}{
		{"github.com", "https://api.github.com/graphql"},
		{"www.github.com", "https://api.github.com/graphql"},
		{"GITHUB.com", "https://api.github.com/graphql"},
		{"ghe.example.com", "https://ghe.example.com/api/graphql"},
		{"ghe.example.com:8443", "https://ghe.example.com:8443/api/graphql"},
	}

	for _, c := range cases {
		t.Run(c.host, func(t *testing.T) {
			assert.Equal(t, c.expected, graphQLEndpoint(c.host))
		})
	}
}

func TestFetchPullRequestsQueryFetchesOnlyAggregateCheckState(t *testing.T) {
	query, variables := fetchPullRequestsQuery([]string{"feature"}, "owner", "repo")

	assert.Contains(t, query, "headRef {")
	assert.Contains(t, query, "... on Commit {")
	assert.Contains(t, query, "statusCheckRollup {")
	assert.NotContains(t, query, "contexts")
	assert.Equal(t, map[string]string{
		"owner":   "owner",
		"repo":    "repo",
		"branch1": "feature",
	}, variables)
}

func TestParsePullRequestsResponse(t *testing.T) {
	t.Run("flattens aliases and normalizes drafts", func(t *testing.T) {
		response := []byte(`{
  "data": {
    "repository": {
      "a1": {
        "edges": [
          {
            "node": {
              "title": "Add feature",
              "headRefName": "feature",
              "number": 42,
              "url": "https://github.com/jesseduffield/lazygit/pull/42",
              "headRepositoryOwner": {"login": "contributor"},
              "state": "OPEN",
              "isDraft": false,
              "headRef": {
                "target": {
                  "statusCheckRollup": {"state": "SUCCESS"}
                }
              }
            }
          }
        ]
      },
      "a2": {
        "edges": [
          {
            "node": {
              "title": "Draft feature",
              "headRefName": "draft-feature",
              "number": 43,
              "url": "https://github.com/jesseduffield/lazygit/pull/43",
              "headRepositoryOwner": {"login": "contributor"},
              "state": "OPEN",
              "isDraft": true,
              "headRef": null
            }
          }
        ]
      }
    }
  }
}`)

		prs, err := parsePullRequestsResponse(response)

		assert.NoError(t, err)
		assert.ElementsMatch(t, []*models.GithubPullRequest{
			{
				HeadRefName:         "feature",
				Number:              42,
				Title:               "Add feature",
				State:               "OPEN",
				ChecksState:         "SUCCESS",
				Url:                 "https://github.com/jesseduffield/lazygit/pull/42",
				HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
			},
			{
				HeadRefName:         "draft-feature",
				Number:              43,
				Title:               "Draft feature",
				State:               "DRAFT",
				Url:                 "https://github.com/jesseduffield/lazygit/pull/43",
				HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
			},
		}, prs)
	})

	t.Run("returns an empty slice for an empty result", func(t *testing.T) {
		prs, err := parsePullRequestsResponse([]byte(`{"data":{"repository":{}}}`))

		assert.NoError(t, err)
		assert.Empty(t, prs)
	})

	t.Run("rejects malformed JSON", func(t *testing.T) {
		prs, err := parsePullRequestsResponse([]byte(`{"data":`))

		assert.Error(t, err)
		assert.Nil(t, prs)
	})
}

func TestGenerateGithubPullRequestMap(t *testing.T) {
	cases := []struct {
		name     string
		prs      []*models.GithubPullRequest
		branches []*models.Branch
		remotes  []*models.Remote
		expected map[string]*models.GithubPullRequest
	}{
		{
			name:     "empty inputs",
			prs:      []*models.GithubPullRequest{},
			branches: []*models.Branch{},
			remotes:  []*models.Remote{},
			expected: map[string]*models.GithubPullRequest{},
		},
		{
			name: "matches PR to branch tracking origin",
			prs: []*models.GithubPullRequest{
				{
					HeadRefName:         "feature-branch",
					Number:              42,
					Title:               "Add feature",
					State:               "OPEN",
					ChecksState:         "PENDING",
					Url:                 "https://github.com/jesseduffield/lazygit/pull/42",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
			},
			branches: []*models.Branch{
				{
					Name:           "feature-branch",
					UpstreamRemote: "origin",
					UpstreamBranch: "feature-branch",
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
				},
			},
			expected: map[string]*models.GithubPullRequest{
				"feature-branch": {
					HeadRefName:         "feature-branch",
					Number:              42,
					Title:               "Add feature",
					State:               "OPEN",
					ChecksState:         "PENDING",
					Url:                 "https://github.com/jesseduffield/lazygit/pull/42",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
			},
		},
		{
			name: "does not match branch without upstream",
			prs: []*models.GithubPullRequest{
				{
					HeadRefName:         "feature-branch",
					Number:              42,
					Title:               "Add feature",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
			},
			branches: []*models.Branch{
				{
					Name: "feature-branch",
					// no upstream set
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
				},
			},
			expected: map[string]*models.GithubPullRequest{},
		},
		{
			name: "matches fork PR to branch tracking fork remote",
			prs: []*models.GithubPullRequest{
				{
					HeadRefName:         "fix-bug",
					Number:              99,
					Title:               "Fix bug",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
				},
			},
			branches: []*models.Branch{
				{
					Name:           "fix-bug",
					UpstreamRemote: "contributor",
					UpstreamBranch: "fix-bug",
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
				},
				{
					Name: "contributor",
					Urls: []string{"git@github.com:contributor/lazygit.git"},
				},
			},
			expected: map[string]*models.GithubPullRequest{
				"fix-bug": {
					HeadRefName:         "fix-bug",
					Number:              99,
					Title:               "Fix bug",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
				},
			},
		},
		{
			name: "does not match when owner differs",
			prs: []*models.GithubPullRequest{
				{
					HeadRefName:         "feature-branch",
					Number:              42,
					Title:               "Add feature",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "someone-else"},
				},
			},
			branches: []*models.Branch{
				{
					Name:           "feature-branch",
					UpstreamRemote: "origin",
					UpstreamBranch: "feature-branch",
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
				},
			},
			expected: map[string]*models.GithubPullRequest{},
		},
		{
			name: "matches when UpstreamRemote is a full URL",
			prs: []*models.GithubPullRequest{
				{
					HeadRefName:         "my-branch",
					Number:              55,
					Title:               "Full URL upstream",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
				},
			},
			branches: []*models.Branch{
				{
					Name:           "my-branch",
					UpstreamRemote: "git@github.com:contributor/lazygit.git",
					UpstreamBranch: "my-branch",
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
				},
			},
			expected: map[string]*models.GithubPullRequest{
				"my-branch": {
					HeadRefName:         "my-branch",
					Number:              55,
					Title:               "Full URL upstream",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "contributor"},
				},
			},
		},
		{
			name: "uses first PR when branch name is reused (API returns newest first)",
			prs: []*models.GithubPullRequest{
				// API returns newest first (CREATED_AT DESC)
				{
					HeadRefName:         "update-sponsors",
					Number:              50,
					Title:               "Newest PR",
					State:               "CLOSED",
					Url:                 "https://github.com/jesseduffield/lazygit/pull/50",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
				{
					HeadRefName:         "update-sponsors",
					Number:              30,
					Title:               "Middle PR",
					State:               "OPEN",
					Url:                 "https://github.com/jesseduffield/lazygit/pull/30",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
				{
					HeadRefName:         "update-sponsors",
					Number:              10,
					Title:               "Oldest PR",
					State:               "CLOSED",
					Url:                 "https://github.com/jesseduffield/lazygit/pull/10",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
			},
			branches: []*models.Branch{
				{
					Name:           "update-sponsors",
					UpstreamRemote: "origin",
					UpstreamBranch: "update-sponsors",
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"git@github.com:jesseduffield/lazygit.git"},
				},
			},
			expected: map[string]*models.GithubPullRequest{
				"update-sponsors": {
					HeadRefName:         "update-sponsors",
					Number:              50,
					Title:               "Newest PR",
					State:               "CLOSED",
					Url:                 "https://github.com/jesseduffield/lazygit/pull/50",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
			},
		},
		{
			name: "matches with HTTPS remote URL",
			prs: []*models.GithubPullRequest{
				{
					HeadRefName:         "my-pr",
					Number:              10,
					Title:               "My PR",
					State:               "MERGED",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
			},
			branches: []*models.Branch{
				{
					Name:           "my-pr",
					UpstreamRemote: "origin",
					UpstreamBranch: "my-pr",
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"https://github.com/jesseduffield/lazygit.git"},
				},
			},
			expected: map[string]*models.GithubPullRequest{
				"my-pr": {
					HeadRefName:         "my-pr",
					Number:              10,
					Title:               "My PR",
					State:               "MERGED",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "jesseduffield"},
				},
			},
		},
		{
			name: "matches when owner casing differs",
			prs: []*models.GithubPullRequest{
				{
					HeadRefName:         "fix-case-insensitive",
					Number:              42,
					Title:               "Fix case insensitive",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "Jesseduffield"}, // Uppercase J
				},
			},
			branches: []*models.Branch{
				{
					Name:           "fix-case-insensitive",
					UpstreamRemote: "origin",
					UpstreamBranch: "fix-case-insensitive",
				},
			},
			remotes: []*models.Remote{
				{
					Name: "origin",
					Urls: []string{"git@github.com:jesseduffield/lazygit.git"}, // Lowercase j
				},
			},
			expected: map[string]*models.GithubPullRequest{
				"fix-case-insensitive": {
					HeadRefName:         "fix-case-insensitive",
					Number:              42,
					Title:               "Fix case insensitive",
					State:               "OPEN",
					HeadRepositoryOwner: models.GithubRepositoryOwner{Login: "Jesseduffield"},
				},
			},
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			result := GenerateGithubPullRequestMap(c.prs, c.branches, c.remotes)
			assert.Equal(t, c.expected, result)
		})
	}
}
