<%- package model_package -%>

<%-	 store_name = "#{model.name}Store" -%>
<%-
			import "context"
			import "fmt"
			import "google.golang.org/appengine/datastore"
			import "google.golang.org/appengine/log"
			import config.model_package_path
			import :goon, "#{config.store_package_path}/goon_store"

      model_name = "model.#{model.name}"
-%>
type <%= store_name %>Binder interface {
	Query(q *datastore.Query) *datastore.Query
	Visible(m *model.<%= model.name %>) bool
	Prepare(m *model.<%= model.name %>)
}

type <%= store_name %> struct{
	Binder <%= store_name %>Binder
<%- if model.parent -%>
	ParentKey *datastore.Key
<%- end -%>
}

func (s *<%= store_name %>) All(ctx context.Context) ([]*<%= model_name %>, error) {
	return s.Select(ctx, s.Query(ctx))
}

func (s *<%= store_name %>) Select(ctx context.Context, q *datastore.Query) ([]*<%= model_name %>, error) {
	g := goon.FromContext(ctx)
	r := []*<%= model_name %>{}
	log.Infof(ctx, "q is %v\n", q)
	_, err := g.GetAll(q.EventualConsistency(), &r)
	if err != nil {
		log.Errorf(ctx, "Failed to Select <%= model_name %> because of %v\n", err)
		return nil, err
	}
	return r, nil
}

func (s *<%= store_name %>) CountBy(ctx context.Context, q *datastore.Query) (int, error) {
	g := goon.FromContext(ctx)
	c, err := g.Count(q)
	if err != nil {
		log.Errorf(ctx, "Failed to count <%= model_name %> with %v because of %v\n", q, err)
		return 0, err
	}
	return c, nil
}

func (s *<%= store_name %>) Query(ctx context.Context) *datastore.Query {
	g := goon.FromContext(ctx)
	k := g.Kind(new(<%= model_name %>))
	// log.Infof(ctx, "Kind for <%= model_name %> is %v\n", k)
	q := datastore.NewQuery(k)
	if s.Binder != nil {
		q = s.Binder.Query(q)
	}
	return q
}

func (s *<%= store_name %>) ByID(ctx context.Context, <%= model.id_name_var %> <%= model.id_golang_type %>) (*<%= model_name %>, error) {
<%- if model.parent -%>
	r := <%= model_name %>{ParentKey: s.ParentKey, <%= model.id_name %>: <%= model.id_name_var %>}
<%- else -%>
	r := <%= model_name %>{<%= model.id_name %>: <%= model.id_name_var %>}
<%- end -%>
	err := s.Get(ctx, &r)
	if err != nil {
		return nil, err
	}
	return &r, nil
}

func (s *<%= store_name %>) ByKey(ctx context.Context, key *datastore.Key) (*<%= model_name %>, error) {
	if err := s.IsValidKey(ctx, key); err != nil {
		log.Errorf(ctx, "<%= store_name %>.ByKey got Invalid key: %v because of %v\n", key, err)
		return nil, err
	}

	return s.ByID(ctx, key.IntID())
}

func (s *<%= store_name %>) Get(ctx context.Context, m *<%= model_name %>) error {
	g := goon.FromContext(ctx)
	err := g.Get(m)
	if err != nil {
		log.Errorf(ctx, "Failed to Get <%= model_name %> because of %v\n", err)
		return err
	}
<%- if model.parent -%>
	if err := s.ValidateParent(m); err != nil {
		log.Errorf(ctx, "Invalid parent key for <%= model_name %> because of %v\n", err)
		return err
	}
<%- end -%>

	if s.Binder != nil && !s.Binder.Visible(m) {
		return datastore.ErrNoSuchEntity
	}

  return nil
}

func (s *<%= store_name %>) IsValidKey(ctx context.Context, key *datastore.Key) error {
	if key == nil {
		return fmt.Errorf("key is nil")
	}
	g := goon.FromContext(ctx)
	expected := g.Kind(&<%= model_name %>{})
	if key.Kind() != expected {
		return fmt.Errorf("key kind must be %s but was %s", expected, key.Kind())
	}
<%- if model.parent -%>
	if key.Parent() == nil {
		return fmt.Errorf("key parent must not be nil but was nil")
	}
<%- end -%>
	return nil
}

func (s *<%= store_name %>) Exist(ctx context.Context, m *<%= model_name %>) (bool, error) {
	if m.ID == <%= model.id_field.zero_value_expression %> {
		return false, nil
	}
	g := goon.FromContext(ctx)
	key, err := g.KeyError(m)
	if err != nil {
		log.Errorf(ctx, "Failed to Get Key of %v because of %v\n", m, err)
		return false, err
	}
	_, err = s.ByKey(ctx, key)
	if err == datastore.ErrNoSuchEntity {
		return false, nil
	} else if err != nil {
		log.Errorf(ctx, "Failed to get existance of %v because of %v\n", m, err)
		return false, err
	} else {
		return true, nil
	}
}

func (s *<%= store_name %>) Create(ctx context.Context, m *<%= model_name %>) (*datastore.Key, error) {
	if err := m.PrepareToCreate(); err != nil {
		return nil, err
	}
	return s.PutWith(ctx, m, func() error {
		exist, err := s.Exist(ctx, m)
		if err != nil {
			return err
		}
		if exist {
			log.Errorf(ctx, "Failed to create %v because of another entity has same key\n", m)
			return fmt.Errorf("Duplicate <%= model.id_name %> error: %q of %v\n", m.<%= model.id_name %>, m)
		}
		return nil
	})
}

func (s *<%= store_name %>) Update(ctx context.Context, m *<%= model_name %>) (*datastore.Key, error) {
	if err := m.PrepareToUpdate(); err != nil {
		return nil, err
	}
	return s.PutWith(ctx, m, func() error {
		exist, err := s.Exist(ctx, m)
		if err != nil {
			return err
		}
		if !exist {
			log.Errorf(ctx, "Failed to update %v because it doesn't exist\n", m)
			return fmt.Errorf("No data to update %q of %v\n", m.<%= model.id_name %>, m)
		}
		return nil
	})
}

func (s *<%= store_name %>) PutWith(ctx context.Context, m *<%= model_name %>, f func() error) (*datastore.Key, error) {
	if s.Binder != nil {
		s.Binder.Prepare(m)
	}
	if err := s.Validate(ctx, m); err != nil {
		return nil, err
	}
	if f != nil {
		if err := f(); err != nil {
			return nil, err
		}
	}

	return s.Put(ctx, m)
}

func (s *<%= store_name %>) Put(ctx context.Context, m *<%= model_name %>) (*datastore.Key, error) {
<%- if model.goon['id_type'] == 'UUID' -%>
<%-	 import "github.com/goadesign/goa/uuid" -%>
	if m.Id == "" {
		m.Id = uuid.NewV4().String()
	}
<%- end -%>
<%- if model.parent -%>
	if err := s.ValidateParent(m); err != nil {
		log.Errorf(ctx, "Invalid parent key for <%= model_name %> because of %v\n", err)
		return nil, err
	}
<%- end -%>
	g := goon.FromContext(ctx)
	key, err := g.Put(m)
	if err != nil {
		log.Errorf(ctx, "Failed to Put %v because of %v\n", m, err)
		return nil, err
	}
	return key, nil
}

<%- if model.parent -%>
func (s *<%= store_name %>) ValidateParent(m *<%= model_name %>) error {
	if s.ParentKey == nil {
		return nil
	}
	if m.ParentKey == nil {
		m.ParentKey = s.ParentKey
	}
	if !s.ParentKey.Equal(m.ParentKey) {
		return fmt.Errorf("Invalid ParentKey for %v", m)
	}
	return nil
}

<%- end -%>
func (s *<%= store_name %>) Delete(ctx context.Context, m *<%= model_name %>) error {
	g := goon.FromContext(ctx)
	key, err := g.KeyError(m)
	if err != nil {
		log.Errorf(ctx, "Failed to Get key of %v because of %v\n", m, err)
		return err
	}
	if err := g.Delete(key); err != nil {
		log.Errorf(ctx, "Failed to Delete %v because of %v\n", m, err)
		return err
	}
	return nil
}

func (s *<%= store_name %>) ValidateUniqueness(ctx context.Context, m *<%= model_name %>) error {
	conditions := map[string]interface{}{
<%- model.fields.select(&:unique?).each do |field| -%>
		"<%= field.name %>": m.<%= field.name %>,
<%- end -%>
	}
	for field, value := range conditions {
		q := s.Query(ctx).Filter(field + " =", value)
		c, err := s.CountBy(ctx, q)
		if err != nil {
			return err
		}
		b := 0
		if m.IsPersisted() {
				b = 1
		}
		if c > b {
			return &model.ValidationError{
				Field: field,
				Message: fmt.Sprintf("%v has already been taken", value),
			}
		}
	}
	return nil
}