package shared

import (
	"net/http"
	"os"
	"path/filepath"
	"testing"

	fd "github.com/cli/cli/v2/internal/featuredetection"
	"github.com/cli/cli/v2/internal/ghrepo"
	"github.com/cli/cli/v2/internal/prompter"
	"github.com/cli/cli/v2/pkg/httpmock"
	"github.com/stretchr/testify/assert"
)

func TestTemplateManager_hasAPI(t *testing.T) {
	rootDir := t.TempDir()
	legacyTemplateFile := filepath.Join(rootDir, ".github", "ISSUE_TEMPLATE.md")
	_ = os.MkdirAll(filepath.Dir(legacyTemplateFile), 0755)
	_ = os.WriteFile(legacyTemplateFile, []byte("LEGACY"), 0644)

	tr := httpmock.Registry{}
	httpClient := &http.Client{Transport: &tr}
	defer tr.Verify(t)

	tr.Register(
		httpmock.GraphQL(`query IssueTemplates\b`),
		httpmock.StringResponse(`{"data":{"repository":{
			"issueTemplates": [
				{"name": "Bug report", "body": "I found a problem", "title": "bug: "},
				{"name": "Feature request", "body": "I need a feature", "title": "request: "}
			]
		}}}`))

	pm := &prompter.PrompterMock{}
	pm.SelectFunc = func(p, _ string, opts []string) (int, error) {
		if p == "Choose a template" {
			return prompter.IndexFor(opts, "Feature request")
		} else {
			return -1, prompter.NoSuchPromptErr(p)
		}
	}

	m := templateManager{
		repo:       ghrepo.NewWithHost("OWNER", "REPO", "example.com"),
		rootDir:    rootDir,
		allowFS:    true,
		isPR:       false,
		httpClient: httpClient,
		detector:   &fd.EnabledDetectorMock{},
		prompter:   pm,
	}

	hasTemplates, err := m.HasTemplates()
	assert.NoError(t, err)
	assert.True(t, hasTemplates)

	assert.Equal(t, "LEGACY", string(m.LegacyBody()))

	tpl, err := m.Choose()

	assert.NoError(t, err)
	assert.Equal(t, "Feature request", tpl.NameForSubmit())
	assert.Equal(t, "I need a feature", string(tpl.Body()))
	assert.Equal(t, "request: ", tpl.Title())
}

func TestTemplateManager_hasAPI_PullRequest(t *testing.T) {
	rootDir := t.TempDir()
	legacyTemplateFile := filepath.Join(rootDir, ".github", "PULL_REQUEST_TEMPLATE.md")
	_ = os.MkdirAll(filepath.Dir(legacyTemplateFile), 0755)
	_ = os.WriteFile(legacyTemplateFile, []byte("LEGACY"), 0644)

	tr := httpmock.Registry{}
	httpClient := &http.Client{Transport: &tr}
	defer tr.Verify(t)

	tr.Register(
		httpmock.GraphQL(`query PullRequestTemplates\b`),
		httpmock.StringResponse(`{"data":{"repository":{
			"pullRequestTemplates": [
				{"filename": "bug_pr.md", "body": "I fixed a problem"},
				{"filename": "feature_pr.md", "body": "I added a feature"}
			]
		}}}`))

	pm := &prompter.PrompterMock{}
	pm.SelectFunc = func(p, _ string, opts []string) (int, error) {
		if p == "Choose a template" {
			return prompter.IndexFor(opts, "bug_pr.md")
		} else {
			return -1, prompter.NoSuchPromptErr(p)
		}
	}
	m := templateManager{
		repo:       ghrepo.NewWithHost("OWNER", "REPO", "example.com"),
		rootDir:    rootDir,
		allowFS:    true,
		isPR:       true,
		httpClient: httpClient,
		detector:   &fd.EnabledDetectorMock{},
		prompter:   pm,
	}

	hasTemplates, err := m.HasTemplates()
	assert.NoError(t, err)
	assert.True(t, hasTemplates)

	assert.Equal(t, "LEGACY", string(m.LegacyBody()))

	tpl, err := m.Choose()

	assert.NoError(t, err)
	assert.Equal(t, "", tpl.NameForSubmit())
	assert.Equal(t, "I fixed a problem", string(tpl.Body()))
	assert.Equal(t, "", tpl.Title())
}

func TestTemplateManagerSelect(t *testing.T) {
	tests := []struct {
		name         string
		isPR         bool
		templateName string
		wantTemplate Template
		wantErr      bool
		errMsg       string
		httpStubs    func(*httpmock.Registry)
	}{
		{
			name:         "no templates found",
			templateName: "Bug report",
			httpStubs: func(reg *httpmock.Registry) {
				reg.Register(
					httpmock.GraphQL(`query IssueTemplates\b`),
					httpmock.StringResponse(`{"data":{"repository":{"issueTemplates":[]}}}`),
				)
			},
			wantErr: true,
			errMsg:  "no templates found",
		},
		{
			name:         "no matching templates found",
			templateName: "Unknown report",
			httpStubs: func(reg *httpmock.Registry) {
				reg.Register(
					httpmock.GraphQL(`query IssueTemplates\b`),
					httpmock.StringResponse(`
					{ "data": { "repository": { "issueTemplates": [
						{ "name": "Bug report", "body": "I found a problem" },
						{ "name": "Feature request", "body": "I need a feature" }
					] } } }`),
				)
			},
			wantErr: true,
			errMsg:  `template "Unknown report" not found`,
		},
		{
			name:         "matching issue template found",
			templateName: "Bug report",
			httpStubs: func(reg *httpmock.Registry) {
				reg.Register(
					httpmock.GraphQL(`query IssueTemplates\b`),
					httpmock.StringResponse(`
					{ "data": { "repository": { "issueTemplates": [
						{ "name": "Bug report", "body": "I found a problem" },
						{ "name": "Feature request", "body": "I need a feature" }
					] } } }`),
				)
			},
			wantTemplate: &issueTemplate{
				Gname: "Bug report",
				Gbody: "I found a problem",
			},
		},
		{
			name:         "matching pull request template found",
			isPR:         true,
			templateName: "feature.md",
			httpStubs: func(reg *httpmock.Registry) {
				reg.Register(
					httpmock.GraphQL(`query PullRequestTemplates\b`),
					httpmock.StringResponse(`
					{ "data": { "repository": { "PullRequestTemplates": [
						{ "filename": "bug.md", "body": "I fixed a problem" },
						{ "filename": "feature.md", "body": "I made a feature" }
					] } } }`),
				)
			},
			wantTemplate: &pullRequestTemplate{
				Gname: "feature.md",
				Gbody: "I made a feature",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			reg := &httpmock.Registry{}
			defer reg.Verify(t)
			if tt.httpStubs != nil {
				tt.httpStubs(reg)
			}

			m := templateManager{
				repo:       ghrepo.NewWithHost("OWNER", "REPO", "example.com"),
				allowFS:    false,
				isPR:       tt.isPR,
				httpClient: &http.Client{Transport: reg},
				detector:   &fd.EnabledDetectorMock{},
			}

			tmpl, err := m.Select(tt.templateName)

			if tt.wantErr {
				assert.EqualError(t, err, tt.errMsg)
			} else {
				assert.NoError(t, err)
			}
			if tt.wantTemplate != nil {
				assert.Equal(t, tt.wantTemplate.Name(), tmpl.Name())
				assert.Equal(t, tt.wantTemplate.Body(), tmpl.Body())
			}
		})
	}
}
