package authmetadata

import (
	"fmt"
	"reflect"
	"sort"
	"testing"

	"github.com/hashicorp/vault/sdk/framework"
	"github.com/hashicorp/vault/sdk/logical"
)

var testFields = &Fields{
	FieldName:      "some-field-name",
	Default:        []string{"fizz", "buzz"},
	AvailableToAdd: []string{"foo", "bar"},
}

func TestFieldSchema(t *testing.T) {
	schema := FieldSchema(testFields)
	if schema.Type != framework.TypeCommaStringSlice {
		t.Fatal("expected TypeCommaStringSlice")
	}
	if schema.Description != `The metadata to include on the aliases and audit logs generated by this plugin. When set to 'default', includes: fizz, buzz. These fields are available to add: foo, bar. Not editing this field means the 'default' fields are included. Explicitly setting this field to empty overrides the 'default' and means no metadata will be included. If not using 'default', explicit fields must be sent like: 'field1,field2'.` {
		t.Fatal("received unexpected description: " + schema.Description)
	}
	if schema.DisplayAttrs == nil {
		t.Fatal("expected display attributes")
	}
	if schema.DisplayAttrs.Name != testFields.FieldName {
		t.Fatalf("expected name of %s", testFields.FieldName)
	}
	if schema.DisplayAttrs.Value != "field1,field2" {
		t.Fatal("expected field1,field2")
	}
	if !reflect.DeepEqual(schema.Default, []string{"default"}) {
		t.Fatal("expected default")
	}
}

func TestGetAuthMetadata(t *testing.T) {
	h := NewHandler(testFields)
	expected := []string{"fizz", "buzz"}
	sort.Strings(expected)
	actual := h.AuthMetadata()
	sort.Strings(actual)
	if !reflect.DeepEqual(expected, actual) {
		t.Fatalf("expected %s but received %s", expected, actual)
	}
}

func TestParseAuthMetadata(t *testing.T) {
	h := NewHandler(testFields)
	data := &framework.FieldData{
		Raw: map[string]interface{}{
			testFields.FieldName: []string{"default"},
		},
		Schema: map[string]*framework.FieldSchema{
			testFields.FieldName: FieldSchema(testFields),
		},
	}
	if err := h.ParseAuthMetadata(data); err != nil {
		t.Fatal(err)
	}
	expected := []string{"fizz", "buzz"}
	sort.Strings(expected)
	actual := h.AuthMetadata()
	sort.Strings(actual)
	if !reflect.DeepEqual(expected, actual) {
		t.Fatalf("expected %s but received %s", expected, actual)
	}
}

func TestPopulateDesiredAuthMetadata(t *testing.T) {
	h := NewHandler(testFields)
	data := &framework.FieldData{
		Raw: map[string]interface{}{
			testFields.FieldName: []string{"foo"},
		},
		Schema: map[string]*framework.FieldSchema{
			testFields.FieldName: FieldSchema(testFields),
		},
	}
	if err := h.ParseAuthMetadata(data); err != nil {
		t.Fatal(err)
	}
	auth := &logical.Auth{
		Alias: &logical.Alias{
			Name: "foo",
		},
	}
	if err := h.PopulateDesiredMetadata(auth, map[string]string{
		"fizz": "fizzval",
		"buzz": "buzzval",
		"foo":  "fooval",
	}); err != nil {
		t.Fatal(err)
	}
	if len(auth.Alias.Metadata) != 1 {
		t.Fatal("expected only 1 configured field to be populated")
	}
	if auth.Alias.Metadata["foo"] != "fooval" {
		t.Fatal("expected foova;")
	}
}

func TestMarshalJSON(t *testing.T) {
	h := NewHandler(&Fields{})
	h.authMetadata = []string{"fizz", "buzz"}
	b, err := h.MarshalJSON()
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != `{"auth_metadata":["fizz","buzz"]}` {
		t.Fatal(`expected {"auth_metadata":["fizz","buzz"]}`)
	}
}

func TestUnmarshalJSON(t *testing.T) {
	h := NewHandler(&Fields{})
	if err := h.UnmarshalJSON([]byte(`{"auth_metadata":["fizz","buzz"]}`)); err != nil {
		t.Fatal(err)
	}
	if fmt.Sprintf("%s", h.authMetadata) != `[fizz buzz]` {
		t.Fatal(`expected [fizz buzz]`)
	}
}
