package test

import (
	"testing"

	"github.com/mgechev/revive/lint"
	"github.com/mgechev/revive/rule"
)

func TestStringFormat(t *testing.T) {
	testRule(t, "string-format", &rule.StringFormatRule{}, &lint.RuleConfig{
		Arguments: lint.Arguments{
			[]interface{}{
				"stringFormatMethod1", // The first argument is checked by default
				"/^[A-Z]/",
				"must start with a capital letter"},

			[]interface{}{
				"stringFormatMethod2[2].d",
				"/[^\\.]$/"}, // Must not end with a period
			[]interface{}{
				"s.Method3[2]",
				"!/^[Tt][Hh]/",
				"must not start with 'th'"}}})
}

func TestStringFormatArgumentParsing(t *testing.T) {
	r := &rule.StringFormatRule{}
	type argumentsTest struct {
		name          string
		config        lint.Arguments
		expectedError *string
	}
	stringPtr := func(s string) *string {
		return &s
	}
	tests := []argumentsTest{
		{
			name: "Not a Slice",
			config: lint.Arguments{
				"this is not a slice"},
			expectedError: stringPtr("invalid configuration for string-format: argument is not a slice [argument 0, option 0]")},
		{
			name: "Missing Regex",
			config: lint.Arguments{
				[]interface{}{
					"method[0]"}},
			expectedError: stringPtr("invalid configuration for string-format: less than two slices found in argument, scope and regex are required [argument 0, option 0]")},
		{
			name: "Bad Argument Type",
			config: lint.Arguments{
				[]interface{}{
					1}},
			expectedError: stringPtr("invalid configuration for string-format: less than two slices found in argument, scope and regex are required [argument 0, option 0]")},
		{
			name: "Empty Scope",
			config: lint.Arguments{
				[]interface{}{
					"",
					"//"}},
			expectedError: stringPtr("invalid configuration for string-format: empty scope provided [argument 0, option 0]")},
		{
			name: "Small or Empty Regex",
			config: lint.Arguments{
				[]interface{}{
					"method[1].a",
					"-"}},
			expectedError: stringPtr("invalid configuration for string-format: regex is too small (regexes should begin and end with '/') [argument 0, option 1]")},
		{
			name: "Bad Scope",
			config: lint.Arguments{
				[]interface{}{
					"1.a",
					"//"}},
			expectedError: stringPtr("failed to parse configuration for string-format: unable to parse rule scope [argument 0, option 0]")},
		{
			name: "Bad Regex",
			config: lint.Arguments{
				[]interface{}{
					"method[1].a",
					"/(/"}},
			expectedError: stringPtr("failed to parse configuration for string-format: unable to compile /(/ as regexp [argument 0, option 1]")},
		{
			name: "Sample Config",
			config: lint.Arguments{
				[]interface{}{
					"core.WriteError[1].Message", "/^([^A-Z]$)/", "must not start with a capital letter"},
				[]interface{}{
					"fmt.Errorf[0]", "/^|[^\\.!?]$/", "must not end in punctuation"},
				[]interface{}{
					"panic", "/^[^\\n]*$/", "must not contain line breaks"}}},
		{
			name: "Underscores in Scope",
			config: lint.Arguments{
				[]interface{}{
					"some_pkg._some_function_name[5].some_member",
					"//"}}}}

	for _, a := range tests {
		t.Run(a.name, func(t *testing.T) {
			err := r.ParseArgumentsTest(a.config)
			if err != nil {
				if a.expectedError == nil || *err != *a.expectedError {
					t.Errorf("unexpected panic message: %s", *err)
				}
			} else if a.expectedError != nil {
				t.Error("error expected but not received")
			}
		})
	}
}
