package auth

import "testing"

var scopeStores = []scopeStore{NewMemstore()}

func compareScopes(scope1, scope2 Scope) (success bool, field string, val1, val2 interface{}) {
	if scope1.ID != scope2.ID {
		return false, "ID", scope1.ID, scope2.ID
	}
	if scope1.Name != scope2.Name {
		return false, "Name", scope1.Name, scope2.Name
	}
	if scope1.Description != scope2.Description {
		return false, "Description", scope1.Description, scope2.Description
	}
	return true, "", nil, nil
}

func TestScopeInScopeStore(t *testing.T) {
	scope := Scope{
		ID:          "testscope",
		Name:        "Test Scope",
		Description: "Access to testing data.",
	}
	scope2 := Scope{
		ID:          "testscope2",
		Name:        "Test Scope 2",
		Description: "Access to minions.",
	}
	scope3 := Scope{
		ID:          "testscope3",
		Name:        "Test Scope 3",
		Description: "Access to bananas.",
	}
	updatedName := "Updated Scope"
	updatedDescription := "An updated scope."
	update := ScopeChange{
		Name: &updatedName,
	}
	update2 := ScopeChange{
		Description: &updatedDescription,
	}
	update3 := ScopeChange{
		Name:        &updatedName,
		Description: &updatedDescription,
	}
	for _, store := range scopeStores {
		context := Context{scopes: store}
		retrieved, err := context.GetScopes([]string{scope.ID})
		if len(retrieved) != 0 {
			t.Logf("%+v", retrieved)
			t.Errorf("Expected %d results, got %d from %T", 0, len(retrieved), store)
		}
		if e, ok := err.(ErrScopeNotFound); !ok {
			t.Errorf("Expected ErrScopeNotFound, got %+v instead for %T", err, store)
		} else {
			if e.Pos != 0 {
				t.Errorf("Expected the error to be in position %d, got position %d from %T", 0, e.Pos, store)
			}
			if e.ID != scope.ID {
				t.Errorf("Expected the error to be with scope %s, got %s from %T", scope.ID, e.ID, store)
			}
		}
		err = context.CreateScopes([]Scope{scope})
		if err != nil {
			t.Errorf("Error saving scope to %T: %s", store, err)
		}
		err = context.CreateScopes([]Scope{scope})
		if e, ok := err.(ErrScopeAlreadyExists); !ok {
			t.Errorf("Expected ErrScopeAlreadyExists, got %s instead for %T", err, store)
		} else {
			if e.Pos != 0 {
				t.Errorf("Expected the error to be in position %d, got position %d from %T", 0, e.Pos, store)
			}
			if e.ID != scope.ID {
				t.Errorf("Expected the error to be for ID %s, got %s from %T", scope.ID, e.ID, store)
			}
		}
		retrieved, err = context.GetScopes([]string{scope.ID})
		if err != nil {
			t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err)
		}
		if len(retrieved) != 1 {
			t.Logf("%+v", retrieved)
			t.Errorf("Expected %d results, got %d from %T", 1, len(retrieved), store)
		}
		success, field, val1, val2 := compareScopes(scope, retrieved[0])
		if !success {
			t.Errorf("Expected %s to be %+v, got %+v from %T", field, val1, val2, store)
		}
		err = context.CreateScopes([]Scope{scope2, scope3})
		if err != nil {
			t.Errorf("Unexpected error trying to create scope2 and scope3 in %T: %+v", store, err)
		}
		retrieved, err = context.GetScopes([]string{scope.ID, scope2.ID, scope3.ID})
		if err != nil {
			t.Errorf("Unexpected error retrieving scopes from  %T: %+v", store, err)
		}
		if len(retrieved) != 3 {
			t.Logf("%+v", retrieved)
			t.Errorf("Expected %d results, got %d from %T", 3, len(retrieved), store)
		}
		for pos, s := range []Scope{scope, scope2, scope3} {
			success, field, val1, val2 = compareScopes(s, retrieved[pos])
			if !success {
				t.Errorf("Expected %s to be %+v for scope %s, got %+v from %T", field, val1, s.ID, val2, store)
			}
		}
		err = context.UpdateScope(scope.ID, update)
		if err != nil {
			t.Errorf("Unexpected error updating scope in %T: %+v", store, err)
		}
		scope.ApplyChange(update)
		err = context.UpdateScope(scope2.ID, update2)
		if err != nil {
			t.Errorf("Unexpected error updating scope in %T: %+v", store, err)
		}
		scope2.ApplyChange(update2)
		err = context.UpdateScope(scope3.ID, update3)
		if err != nil {
			t.Errorf("Unexpected error updating scope in %T: %+v", store, err)
		}
		scope3.ApplyChange(update3)
		retrieved, err = context.ListScopes()
		if err != nil {
			t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err)
		}
		if len(retrieved) != 3 {
			t.Logf("%+v", retrieved)
			t.Errorf("Expected %d results, got %d from %T", 3, len(retrieved), store)
		}
		for pos, s := range []Scope{scope, scope2, scope3} {
			success, field, val1, val2 = compareScopes(s, retrieved[pos])
			if !success {
				t.Errorf("Expected %s to be %+v for scope %s, got %+v from %T", field, val1, s.ID, val2, store)
			}
		}
		err = context.RemoveScopes([]string{scope.ID, scope2.ID, scope3.ID})
		if err != nil {
			t.Errorf("Unexpected error removing scopes from %T: %+v", store, err)
		}
		retrieved, err = context.ListScopes()
		if err != nil {
			t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err)
		}
		if len(retrieved) != 0 {
			t.Logf("%+v", retrieved)
			t.Errorf("Expected %d results, got %d from %T", 0, len(retrieved), store)
		}
		err = context.RemoveScopes([]string{scope.ID})
		if err == nil {
			t.Errorf("No error returned removing non-existent scopes from %T", store)
		}
		if e, ok := err.(ErrScopeNotFound); !ok {
			t.Errorf("Unexpected error removing non-existent scopes from %T: %+v", store, err)
		} else {
			if e.Pos != 0 {
				t.Errorf("Expected error to be for scope ID in pos %d, but got %d from %T", 0, e.Pos, store)
			}
			if e.ID != scope.ID {
				t.Errorf("Expected error to be for scope ID %s, but got %s from %T", scope.ID, e.ID, store)
			}
		}
		err = context.UpdateScope(scope.ID, update)
		if err == nil {
			t.Errorf("No error returned updating non-existent scopes from %T", store)
		}
		if e, ok := err.(ErrScopeNotFound); !ok {
			t.Errorf("Unexpected error updating non-existent scopes from %T: %+v", store, err)
		} else {
			if e.Pos != 0 {
				t.Errorf("Expected error to be for scope ID in pos %d, but got %d from %T", 0, e.Pos, store)
			}
			if e.ID != scope.ID {
				t.Errorf("Expected error to be for scope ID %s, but got %s from %T", scope.ID, e.ID, store)
			}
		}
	}
}

func TestScopeErrors(t *testing.T) {
	errors := map[string]error{
		"scope test couldn't be found": ErrScopeNotFound{ID: "test", Pos: 0},
		"scope test already exists":    ErrScopeAlreadyExists{ID: "test", Pos: 0},
	}
	for expectation, e := range errors {
		if e.Error() != expectation {
			t.Errorf("Expected %+v to produce '%s', produced '%s'", e, expectation, e.Error())
		}
	}
}
