// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package random

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/mitchellh/mapstructure"
)

type testCharsetRule struct {
	String  string `mapstructure:"string" json:"string"`
	Integer int    `mapstructure:"int"    json:"int"`

	// Default to passing
	fail bool
}

func newTestRule(data map[string]interface{}) (rule Rule, err error) {
	tr := &testCharsetRule{}
	err = mapstructure.Decode(data, tr)
	if err != nil {
		return nil, fmt.Errorf("unable to decode test rule")
	}
	return *tr, nil
}

func (tr testCharsetRule) Pass([]rune) bool { return !tr.fail }
func (tr testCharsetRule) Type() string     { return "testrule" }
func (tr testCharsetRule) Chars() []rune    { return []rune(tr.String) }

func TestParseRule(t *testing.T) {
	type testCase struct {
		rules map[string]ruleConstructor

		ruleType string
		ruleData map[string]interface{}

		expectedRule Rule
		expectErr    bool
	}

	tests := map[string]testCase{
		"missing rule": {
			rules:    map[string]ruleConstructor{},
			ruleType: "testrule",
			ruleData: map[string]interface{}{
				"string": "teststring",
				"int":    123,
			},
			expectedRule: nil,
			expectErr:    true,
		},
		"nil data": {
			rules: map[string]ruleConstructor{
				"testrule": newTestRule,
			},
			ruleType:     "testrule",
			ruleData:     nil,
			expectedRule: testCharsetRule{},
			expectErr:    false,
		},
		"good rule": {
			rules: map[string]ruleConstructor{
				"testrule": newTestRule,
			},
			ruleType: "testrule",
			ruleData: map[string]interface{}{
				"string": "teststring",
				"int":    123,
			},
			expectedRule: testCharsetRule{
				String:  "teststring",
				Integer: 123,
			},
			expectErr: false,
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			reg := Registry{
				Rules: test.rules,
			}

			actualRule, err := reg.parseRule(test.ruleType, test.ruleData)
			if test.expectErr && err == nil {
				t.Fatalf("err expected, got nil")
			}
			if !test.expectErr && err != nil {
				t.Fatalf("no error expected, got: %s", err)
			}

			if !reflect.DeepEqual(actualRule, test.expectedRule) {
				t.Fatalf("Actual: %#v\nExpected:%#v", actualRule, test.expectedRule)
			}
		})
	}
}

// Ensure the mappings in the defaultRuleNameMapping are consistent between the keys
// in the map and the Type() calls on the Rule values
func TestDefaultRuleNameMapping(t *testing.T) {
	for expectedType, constructor := range defaultRuleNameMapping {
		// In this case, we don't care about the error since we're checking the types, not the contents
		instance, _ := constructor(map[string]interface{}{})
		actualType := instance.Type()
		if actualType != expectedType {
			t.Fatalf("Default registry mismatched types: Actual: %s Expected: %s", actualType, expectedType)
		}
	}
}
