package bolt

import (
	"context"

	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/kv/internal"
	bolt "go.etcd.io/bbolt"
)

// Ensure buildStore struct implements chronograf.BuildStore interface.
var _ chronograf.BuildStore = &buildStore{}

// buildBucket is the bolt bucket used to store Chronograf build information
var buildBucket = []byte("Build")

// buildKey is the constant key used in the bolt bucket
var buildKey = []byte("build")

var defaultBuildInfo = chronograf.BuildInfo{
	Version: "1.8.0",
	Commit:  "",
}

// buildStore is a bolt implementation to store Chronograf build information
type buildStore struct {
	client *client
}

// Get retrieves Chronograf build information from the database
func (s *buildStore) Get(ctx context.Context) (chronograf.BuildInfo, error) {
	var build chronograf.BuildInfo
	if err := s.client.db.View(func(tx *bolt.Tx) error {
		var err error
		build, err = getBuildInfo(ctx, tx)
		if err != nil {
			return err
		}
		return nil
	}); err != nil {
		return build, err
	}

	return build, nil
}

// getBuildInfo retrieves the current build, falling back to a default when missing
func getBuildInfo(ctx context.Context, tx *bolt.Tx) (chronograf.BuildInfo, error) {
	var build chronograf.BuildInfo

	if bucket := tx.Bucket(buildBucket); bucket == nil {
		return defaultBuildInfo, nil
	} else if v := bucket.Get(buildKey); v == nil {
		return defaultBuildInfo, nil
	} else if err := internal.UnmarshalBuild(v, &build); err != nil {
		return build, err
	}

	return build, nil
}

// Update overwrites the current Chronograf build information in the database
func (s *buildStore) Update(ctx context.Context, build chronograf.BuildInfo) error {
	if err := s.client.db.Update(func(tx *bolt.Tx) error {
		return updateBuildInfo(ctx, build, tx)
	}); err != nil {
		return err
	}

	return nil
}

func updateBuildInfo(ctx context.Context, build chronograf.BuildInfo, tx *bolt.Tx) error {
	if v, err := internal.MarshalBuild(build); err != nil {
		return err
	} else if err := tx.Bucket(buildBucket).Put(buildKey, v); err != nil {
		return err
	}
	return nil
}
