package scopes

import (
	"errors"
	"fmt"

	"golang.org/x/net/context"
)

var (
	ErrNoStorer      = errors.New("Storer not set in Context")
	ErrScopeNotFound = errors.New("Scope not found")
)

type ErrScopeAlreadyExists string

func (e ErrScopeAlreadyExists) Error() string {
	return fmt.Sprintf("scope %s already exists", string(e))
}

// Scope represents a limit on the access that a grant provides.
type Scope struct {
	ID          string
	Name        string
	Description string
}

func ApplyChange(change ScopeChange, scope Scope) Scope {
	changed := scope
	if change.Name != nil {
		changed.Name = *change.Name
	}
	if change.Description != nil {
		changed.Description = *change.Description
	}
	return changed
}

type Scopes []Scope

func (s Scopes) Len() int {
	return len(s)
}

func (s Scopes) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s Scopes) Less(i, j int) bool {
	return s[i].ID < s[j].ID
}

func (s Scopes) Strings() []string {
	res := make([]string, len(s))
	for pos, scope := range s {
		res[pos] = scope.ID
	}
	return res
}

func stringsToScopes(s []string) Scopes {
	res := make(Scopes, len(s))
	for pos, scope := range s {
		res[pos] = Scope{ID: scope}
	}
	return res
}

// ScopeChange represents a change to a Scope.
type ScopeChange struct {
	ScopeID     string
	Name        *string
	Description *string
}

func (s ScopeChange) Empty() bool {
	return s.Name == nil && s.Description == nil
}

type Storer interface {
	CreateScopes(scopes []Scope, ctx context.Context) error
	GetScopes(ids []string, ctx context.Context) (map[string]Scope, error)
	UpdateScope(change ScopeChange, ctx context.Context) error
	RemoveScopes(ids []string, ctx context.Context) error
	ListScopes(ctx context.Context) ([]Scope, error)
}
