package lint

import (
	"go/token"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	"golang.org/x/tools/go/packages"
)

func testdata() string {
	testdata, err := filepath.Abs("testdata")
	if err != nil {
		log.Fatal(err)
	}
	return testdata
}

func lintPackage(t *testing.T, name string) []Problem {
	l := Linter{}
	cfg := &packages.Config{
		Env: append(os.Environ(), "GOPATH="+testdata(), "GO111MODULE=off"),
	}
	ps, err := l.Lint(cfg, []string{name})
	if err != nil {
		t.Fatal(err)
	}
	return ps
}

func trimPosition(pos *token.Position) {
	idx := strings.Index(pos.Filename, "/testdata/src/")
	if idx >= 0 {
		pos.Filename = pos.Filename[idx+len("/testdata/src/"):]
	}
}

func TestErrors(t *testing.T) {
	t.Run("invalid package declaration", func(t *testing.T) {
		ps := lintPackage(t, "broken_pkgerror")
		if len(ps) != 1 {
			t.Fatalf("got %d problems, want 1", len(ps))
		}
		if want := "expected 'package', found pckage"; ps[0].Message != want {
			t.Errorf("got message %q, want %q", ps[0].Message, want)
		}
		if ps[0].Pos.Filename == "" {
			t.Errorf("didn't get useful position")
		}
	})

	t.Run("type error", func(t *testing.T) {
		if runtime.GOOS == "windows" {
			t.Skip("don't deal with Windows line endings or absolute file names")
		}
		ps := lintPackage(t, "broken_typeerror")
		if len(ps) != 1 {
			t.Fatalf("got %d problems, want 1", len(ps))
		}
		trimPosition(&ps[0].Pos)
		want := Problem{
			Pos: token.Position{
				Filename: "broken_typeerror/pkg.go",
				Offset:   42,
				Line:     5,
				Column:   10,
			},
			Message:  "cannot convert \"\" (untyped string constant) to int",
			Check:    "compile",
			Severity: 0,
		}
		if !ps[0].Equal(want) {
			t.Errorf("got %#v, want %#v", ps[0], want)
		}
	})

	t.Run("missing dep", func(t *testing.T) {
		t.Skip("Go 1.12 behaves incorrectly for missing packages")
	})

	t.Run("parse error", func(t *testing.T) {
		if runtime.GOOS == "windows" {
			t.Skip("don't deal with Windows line endings or absolute file names")
		}
		ps := lintPackage(t, "broken_parse")
		if len(ps) != 1 {
			t.Fatalf("got %d problems, want 1", len(ps))
		}

		trimPosition(&ps[0].Pos)
		want := Problem{
			Pos: token.Position{
				Filename: "broken_parse/pkg.go",
				Offset:   13,
				Line:     3,
				Column:   1,
			},
			Message:  "expected declaration, found asd",
			Check:    "compile",
			Severity: 0,
		}
		if !ps[0].Equal(want) {
			t.Errorf("got %#v, want %#v", ps[0], want)
		}
	})
}
