package repo

import (
	"context"
	"errors"

	"github.com/ipfs/go-datastore"
	"github.com/multiformats/go-multiaddr"

	"github.com/filecoin-project/lotus/blockstore"
	"github.com/filecoin-project/lotus/chain/types"
	"github.com/filecoin-project/lotus/storage/sealer/fsutil"
	"github.com/filecoin-project/lotus/storage/sealer/storiface"
)

// BlockstoreDomain represents the domain of a blockstore.
type BlockstoreDomain string

const (
	// UniversalBlockstore represents the blockstore domain for all data.
	// Right now, this includes chain objects (tipsets, blocks, messages), as
	// well as state. In the future, they may get segregated into different
	// domains.
	UniversalBlockstore = BlockstoreDomain("universal")
	HotBlockstore       = BlockstoreDomain("hot")
)

var (
	ErrNoAPIEndpoint     = errors.New("API not running (no endpoint)")
	ErrNoAPIToken        = errors.New("API token not set")
	ErrRepoAlreadyLocked = errors.New("repo is already locked (lotus daemon already running)")
	ErrClosedRepo        = errors.New("repo is no longer open")

	// ErrInvalidBlockstoreDomain is returned by LockedRepo#Blockstore() when
	// an unrecognized domain is requested.
	ErrInvalidBlockstoreDomain = errors.New("invalid blockstore domain")
)

type Repo interface {
	// APIEndpoint returns multiaddress for communication with Lotus API
	APIEndpoint() (multiaddr.Multiaddr, error)

	// APIToken returns JWT API Token for use in operations that require auth
	APIToken() ([]byte, error)

	// Lock locks the repo for exclusive use.
	Lock(RepoType) (LockedRepo, error)
}

type LockedRepo interface {
	// Close closes repo and removes lock.
	Close() error

	// returns the type of this repo
	RepoType() RepoType

	// Returns datastore defined in this repo.
	// The supplied context must only be used to initialize the datastore.
	// The implementation should not retain the context for usage throughout
	// the lifecycle.
	Datastore(ctx context.Context, namespace string) (datastore.Batching, error)

	// Blockstore returns an IPLD blockstore for the requested domain.
	// The supplied context must only be used to initialize the blockstore.
	// The implementation should not retain the context for usage throughout
	// the lifecycle.
	Blockstore(ctx context.Context, domain BlockstoreDomain) (blockstore.Blockstore, error)

	// SplitstorePath returns the path for the SplitStore
	SplitstorePath() (string, error)

	// SqlitePath returns the path for the Sqlite database
	SqlitePath() (string, error)

	// Returns config in this repo
	Config() (interface{}, error)
	SetConfig(func(interface{})) error

	GetStorage() (storiface.StorageConfig, error)
	SetStorage(func(*storiface.StorageConfig)) error
	Stat(path string) (fsutil.FsStat, error)
	DiskUsage(path string) (int64, error)

	// SetAPIEndpoint sets the endpoint of the current API
	// so it can be read by API clients
	SetAPIEndpoint(multiaddr.Multiaddr) error

	// SetAPIToken sets JWT API Token for CLI
	SetAPIToken([]byte) error

	// KeyStore returns store of private keys for Filecoin transactions
	KeyStore() (types.KeyStore, error)

	// Path returns absolute path of the repo
	Path() string

	// Readonly returns true if the repo is readonly
	Readonly() bool
}
