// Copyright (C) The Arvados Authors. All rights reserved.
//
// SPDX-License-Identifier: AGPL-3.0

package keepstore

import (
	"context"
	"io"
	"time"

	"git.arvados.org/arvados.git/sdk/go/arvados"
	"github.com/sirupsen/logrus"
)

// volume is the interface to a back-end storage device.
type volume interface {
	// Return a unique identifier for the backend device. If
	// possible, this should be chosen such that keepstore
	// processes running on different hosts, and accessing the
	// same backend device, will return the same string.
	//
	// This helps keep-balance avoid redundantly downloading
	// multiple index listings for the same backend device.
	DeviceID() string

	// Copy a block from the backend device to writeTo.
	//
	// As with all volume methods, the hash argument is a
	// 32-character hexadecimal string.
	//
	// Data can be written to writeTo in any order, and concurrent
	// calls to writeTo.WriteAt() are allowed.  However, BlockRead
	// must not do multiple writes that intersect with any given
	// byte offset.
	//
	// BlockRead is not expected to verify data integrity.
	//
	// If the indicated block does not exist, or has been trashed,
	// BlockRead must return os.ErrNotExist.
	BlockRead(ctx context.Context, hash string, writeTo io.WriterAt) error

	// Store a block on the backend device, and set its timestamp
	// to the current time.
	//
	// The implementation must ensure that regardless of any
	// errors encountered while writing, a partially written block
	// is not left behind: a subsequent BlockRead call must return
	// either a) the data previously stored under the given hash,
	// if any, or b) os.ErrNotExist.
	BlockWrite(ctx context.Context, hash string, data []byte) error

	// Update the indicated block's stored timestamp to the
	// current time.
	BlockTouch(hash string) error

	// Return the indicated block's stored timestamp.
	Mtime(hash string) (time.Time, error)

	// Mark the indicated block as trash, such that -- unless it
	// is untrashed before time.Now() + BlobTrashLifetime --
	// BlockRead returns os.ErrNotExist and the block is not
	// listed by Index.
	BlockTrash(hash string) error

	// Un-mark the indicated block as trash. If the block has not
	// been trashed, return os.ErrNotExist.
	BlockUntrash(hash string) error

	// Permanently delete all blocks that have been marked as
	// trash for BlobTrashLifetime or longer.
	EmptyTrash()

	// Write an index of all non-trashed blocks available on the
	// backend device whose hash begins with the given prefix
	// (prefix is a string of zero or more hexadecimal digits).
	//
	// Each block is written as "{hash}+{size} {timestamp}\n"
	// where timestamp is a decimal-formatted number of
	// nanoseconds since the UTC Unix epoch.
	//
	// Index should abort and return ctx.Err() if ctx is cancelled
	// before indexing is complete.
	Index(ctx context.Context, prefix string, writeTo io.Writer) error
}

type volumeDriver func(newVolumeParams) (volume, error)

type newVolumeParams struct {
	UUID         string
	Cluster      *arvados.Cluster
	ConfigVolume arvados.Volume
	Logger       logrus.FieldLogger
	MetricsVecs  *volumeMetricsVecs
	BufferPool   *bufferPool
}

// ioStats tracks I/O statistics for a volume or server
type ioStats struct {
	Errors     uint64
	Ops        uint64
	CompareOps uint64
	GetOps     uint64
	PutOps     uint64
	TouchOps   uint64
	InBytes    uint64
	OutBytes   uint64
}