// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package completion

// This file is misnamed; it has no version constraints.
// TODO(adonovan):  fold into completion_test.go

import (
	"testing"

	"golang.org/x/tools/gopls/internal/protocol"
	. "golang.org/x/tools/gopls/internal/test/integration"
	"golang.org/x/tools/internal/testenv"
)

// test generic receivers
func TestGenericReceiver(t *testing.T) {
	const files = `
-- go.mod --
module mod.com

go 1.18
-- main.go --
package main
type SyncMap[K any, V comparable] struct {}
func (s *SyncMap[K,V]) f() {}
type XX[T any] struct {}
type UU[T any] struct {}
func (s SyncMap[XX,string]) g(v UU) {}
`

	tests := []struct {
		pat  string
		want []string
	}{
		{"s .Syn", []string{"SyncMap[K, V]"}},
		{"Map.X", []string{}}, // This is probably wrong, Maybe "XX"?
		{"v U", []string{"UU", "uint", "uint16", "uint32", "uint64", "uint8", "uintptr"}}, // not U[T]
	}
	Run(t, files, func(t *testing.T, env *Env) {
		env.OpenFile("main.go")
		env.Await(env.DoneWithOpen())
		for _, tst := range tests {
			loc := env.RegexpSearch("main.go", tst.pat)
			loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte(tst.pat)))
			completions := env.Completion(loc)
			result := compareCompletionLabels(tst.want, completions.Items)
			if result != "" {
				t.Errorf("%s: wanted %v", result, tst.want)
				for i, g := range completions.Items {
					t.Errorf("got %d %s %s", i, g.Label, g.Detail)
				}
			}
		}
	})
}

func TestFuzzFunc(t *testing.T) {
	// The behavior under test is derived from the std module,
	// not the x/tools/internal/stdlib linked into gopls.
	testenv.NeedsGoCommand1Point(t, 25) // go1.25 added TBF.Attr

	// use the example from the package documentation
	modfile := `
-- go.mod --
module mod.com

go 1.25
`
	part0 := `package foo
import "testing"
func FuzzNone(f *testing.F) {
	f.Add(12) // better not find this f.Add
}
func FuzzHex(f *testing.F) {
	for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
		f.Ad`
	part1 := `d(seed)
	}
	f.F`
	part2 := `uzz(func(t *testing.T, in []byte) {
		enc := hex.EncodeToString(in)
		out, err := hex.DecodeString(enc)
		if err != nil {
		  f.Failed()
		}
		if !bytes.Equal(in, out) {
		  t.Fatalf("%v: round trip: %v, %s", in, out, f.Name())
		}
	})
}
`
	data := modfile + `-- a_test.go --
` + part0 + `
-- b_test.go --
` + part0 + part1 + `
-- c_test.go --
` + part0 + part1 + part2

	tests := []struct {
		file   string
		pat    string
		offset uint32 // UTF16 length from the beginning of pat to what the user just typed
		want   []string
	}{
		// To avoid breaking these assertions as the "testing" package evolves,
		// use an optional (?) suffix for newer symbols.
		{"a_test.go", "f.Ad", 3, []string{"Add", "ArtifactDir?", "Attr"}}, // Attr is 1.25, Artifact is 1.26
		{"c_test.go", " f.F", 4, []string{"Failed"}},
		{"c_test.go", "f.N", 3, []string{"Name"}},
		{"b_test.go", "f.F", 3, []string{"Fuzz(func(t *testing.T, a []byte)", "Fail", "FailNow",
			"Failed", "Fatal", "Fatalf"}},
	}
	Run(t, data, func(t *testing.T, env *Env) {
		for _, test := range tests {
			env.OpenFile(test.file)
			env.Await(env.DoneWithOpen())
			loc := env.RegexpSearch(test.file, test.pat)
			loc.Range.Start.Character += test.offset // character user just typed? will type?
			completions := env.Completion(loc)
			result := compareCompletionLabels(test.want, completions.Items)
			if result != "" {
				t.Errorf("pat=%q <<%s>>", test.pat, result)
				for i, it := range completions.Items {
					t.Errorf("%d got %q %q", i, it.Label, it.Detail)
				}
			}
		}
	})
}
