package scopes

import (
	"os"

	"golang.org/x/net/context"

	"code.secondbit.org/scopes.hg/types"
)
import "testing"

func init() {
	if os.Getenv("PG_TEST_DB") != "" {
		p, err := NewPostgres(os.Getenv("PG_TEST_DB"))
		if err != nil {
			panic(err)
		}
		storers = append(storers, &p)
	}
}

var storers = []Storer{NewMemstore()}

func compareScopes(scope1, scope2 scopeTypes.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 := scopeTypes.Scope{
		ID:          "testscope",
		Name:        "Test Scope",
		Description: "Access to testing data.",
	}
	scope2 := scopeTypes.Scope{
		ID:          "testscope2",
		Name:        "Test Scope 2",
		Description: "Access to minions.",
	}
	scope3 := scopeTypes.Scope{
		ID:          "testscope3",
		Name:        "Test Scope 3",
		Description: "Access to bananas.",
	}
	updatedName := "Updated Scope"
	updatedDescription := "An updated scope."
	update := scopeTypes.ScopeChange{
		ScopeID: scope.ID,
		Name:    &updatedName,
	}
	update2 := scopeTypes.ScopeChange{
		ScopeID:     scope2.ID,
		Description: &updatedDescription,
	}
	update3 := scopeTypes.ScopeChange{
		ScopeID:     scope3.ID,
		Name:        &updatedName,
		Description: &updatedDescription,
	}
	for _, store := range storers {
		ctx := context.Background()
		retrieved, err := store.GetScopes([]string{scope.ID}, ctx)
		if len(retrieved) != 0 {
			t.Logf("%+v", retrieved)
			t.Errorf("Expected %d results, got %d from %T", 0, len(retrieved), store)
		}
		err = store.CreateScopes([]scopeTypes.Scope{scope}, ctx)
		if err != nil {
			t.Errorf("Error saving scope to %T: %s", store, err)
		}
		err = store.CreateScopes([]scopeTypes.Scope{scope}, ctx)
		if _, ok := err.(ErrScopeAlreadyExists); !ok {
			t.Errorf("Expected ErrScopeAlreadyExists, got %s instead for %T", err, store)
		}
		retrieved, err = store.GetScopes([]string{scope.ID}, ctx)
		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)
		}
		if _, ok := retrieved[scope.ID]; !ok {
			t.Logf("%+v", retrieved)
			t.Errorf("Expected %s to be in results from %T, wasn't", scope.ID, store)
		}
		success, field, val1, val2 := compareScopes(scope, retrieved[scope.ID])
		if !success {
			t.Errorf("Expected %s to be %+v, got %+v from %T", field, val1, val2, store)
		}
		err = store.CreateScopes([]scopeTypes.Scope{scope2, scope3}, ctx)
		if err != nil {
			t.Errorf("Unexpected error trying to create scope2 and scope3 in %T: %+v", store, err)
		}
		retrieved, err = store.GetScopes([]string{scope.ID, scope2.ID, scope3.ID}, ctx)
		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 _, s := range []scopeTypes.Scope{scope, scope2, scope3} {
			if _, ok := retrieved[s.ID]; !ok {
				t.Logf("%+v", retrieved)
				t.Errorf("Expected %s to be in results from %T, wasn't", s.ID, store)
			}
			success, field, val1, val2 = compareScopes(s, retrieved[s.ID])
			if !success {
				t.Errorf("Expected %s to be %+v for scope %s, got %+v from %T", field, val1, s.ID, val2, store)
			}
		}
		err = store.UpdateScope(update, ctx)
		if err != nil {
			t.Errorf("Unexpected error updating scope in %T: %+v", store, err)
		}
		scope = scopeTypes.ApplyChange(update, scope)
		err = store.UpdateScope(update2, ctx)
		if err != nil {
			t.Errorf("Unexpected error updating scope in %T: %+v", store, err)
		}
		scope2 = scopeTypes.ApplyChange(update2, scope2)
		err = store.UpdateScope(update3, ctx)
		if err != nil {
			t.Errorf("Unexpected error updating scope in %T: %+v", store, err)
		}
		scope3 = scopeTypes.ApplyChange(update3, scope3)
		results, err := store.ListScopes(ctx)
		if err != nil {
			t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err)
		}
		if len(results) != 3 {
			t.Logf("%+v", results)
			t.Errorf("Expected %d results, got %d from %T", 3, len(results), store)
		}
		for pos, s := range []scopeTypes.Scope{scope, scope2, scope3} {
			success, field, val1, val2 = compareScopes(s, results[pos])
			if !success {
				t.Errorf("Expected %s to be %+v for scope %s, got %+v from %T", field, val1, s.ID, val2, store)
			}
		}
		err = store.RemoveScopes([]string{scope.ID, scope2.ID, scope3.ID}, ctx)
		if err != nil {
			t.Errorf("Unexpected error removing scopes from %T: %+v", store, err)
		}
		results, err = store.ListScopes(ctx)
		if err != nil {
			t.Errorf("Unexpected error retrieving scopes from %T: %+v", store, err)
		}
		if len(results) != 0 {
			t.Logf("%+v", results)
			t.Errorf("Expected %d results, got %d from %T", 0, len(results), store)
		}
		err = store.UpdateScope(update, ctx)
		if err != ErrScopeNotFound {
			t.Errorf("Unexpected error updating non-existent scopes from %T: %+v", store, err)
		}
	}
}
