package mock

import (
	"context"

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

// kvPaths is used to test CRUD and List operations. It is a simplified
// version of the passthrough backend that only accepts string values.
func kvPaths(b *backend) []*framework.Path {
	return []*framework.Path{
		{
			Pattern: "kv/?",
			Callbacks: map[logical.Operation]framework.OperationFunc{
				logical.ListOperation: b.pathKVList,
			},
		},
		{
			Pattern: "kv/" + framework.GenericNameRegex("key"),
			Fields: map[string]*framework.FieldSchema{
				"key":     {Type: framework.TypeString},
				"value":   {Type: framework.TypeString},
				"version": {Type: framework.TypeInt},
			},
			ExistenceCheck: b.pathExistenceCheck,
			Callbacks: map[logical.Operation]framework.OperationFunc{
				logical.ReadOperation:   b.pathKVRead,
				logical.CreateOperation: b.pathKVCreateUpdate,
				logical.UpdateOperation: b.pathKVCreateUpdate,
				logical.DeleteOperation: b.pathKVDelete,
			},
		},
	}
}

func (b *backend) pathExistenceCheck(ctx context.Context, req *logical.Request, data *framework.FieldData) (bool, error) {
	out, err := req.Storage.Get(ctx, req.Path)
	if err != nil {
		return false, errwrap.Wrapf("existence check failed: {{err}}", err)
	}

	return out != nil, nil
}

func (b *backend) pathKVRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	version := data.Get("version").(int)

	entry, err := req.Storage.Get(ctx, req.Path)
	if err != nil {
		return nil, err
	}

	if entry == nil {
		return nil, nil
	}

	value := string(entry.Value)

	b.Logger().Info("reading value", "key", req.Path, "value", value)
	// Return the secret
	resp := &logical.Response{
		Data: map[string]interface{}{
			"value":   value,
			"version": version,
		},
	}
	if version != 0 {
		resp.Data["version"] = version
	}
	return resp, nil
}

func (b *backend) pathKVCreateUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	value := data.Get("value").(string)

	b.Logger().Info("storing value", "key", req.Path, "value", value)
	entry := &logical.StorageEntry{
		Key:   req.Path,
		Value: []byte(value),
	}

	s := req.Storage
	err := s.Put(ctx, entry)
	if err != nil {
		return nil, err
	}

	return &logical.Response{
		Data: map[string]interface{}{
			"value": value,
		},
	}, nil
}

func (b *backend) pathKVDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	if err := req.Storage.Delete(ctx, req.Path); err != nil {
		return nil, err
	}

	return nil, nil
}

func (b *backend) pathKVList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	vals, err := req.Storage.List(ctx, "kv/")
	if err != nil {
		return nil, err
	}
	return logical.ListResponse(vals), nil
}
