package storage

import (
	"context"
	"io"
	"time"
)

// Provider defines the interface for sovereign storage backends.
// Implementations must ensure data sovereignty compliance for government deployments.
//
// Tutus supports multiple storage backends to accommodate national data
// residency requirements:
//   - NeoFS: Decentralized storage (can be deployed as private network)
//   - Local: Filesystem storage for single-node or NAS deployments
//   - S3-compatible: AWS, Azure, GCP, or sovereign cloud providers
//   - Custom: Nation-specific storage infrastructure
type Provider interface {
	// Name returns the provider identifier (e.g., "neofs", "local", "s3")
	Name() string

	// Put stores data and returns a unique identifier for retrieval.
	// The identifier format is provider-specific.
	Put(ctx context.Context, data io.Reader, opts PutOptions) (ObjectID, error)

	// Get retrieves data by its identifier.
	// Returns ErrNotFound if the object doesn't exist.
	Get(ctx context.Context, id ObjectID) (io.ReadCloser, error)

	// Delete removes an object from storage.
	// Returns ErrNotFound if the object doesn't exist.
	Delete(ctx context.Context, id ObjectID) error

	// Exists checks if an object exists in storage.
	Exists(ctx context.Context, id ObjectID) (bool, error)

	// List returns object IDs matching the given prefix.
	// Use an empty prefix to list all objects in the container.
	List(ctx context.Context, prefix string, opts ListOptions) ([]ObjectInfo, error)

	// Head retrieves object metadata without downloading content.
	Head(ctx context.Context, id ObjectID) (ObjectInfo, error)

	// Close releases any resources held by the provider.
	Close() error
}

// BlockStorage extends Provider with blockchain-specific operations.
type BlockStorage interface {
	Provider

	// PutBlock stores a block with its index for efficient retrieval.
	PutBlock(ctx context.Context, index uint32, data []byte) (ObjectID, error)

	// GetBlock retrieves a block by its index.
	GetBlock(ctx context.Context, index uint32) ([]byte, error)

	// GetBlockRange retrieves multiple blocks efficiently.
	GetBlockRange(ctx context.Context, start, end uint32) ([][]byte, error)

	// GetLatestBlockIndex returns the highest block index in storage.
	GetLatestBlockIndex(ctx context.Context) (uint32, error)
}

// StateStorage extends Provider with state snapshot operations.
type StateStorage interface {
	Provider

	// PutState stores a state snapshot at a specific height.
	PutState(ctx context.Context, height uint32, data io.Reader) (ObjectID, error)

	// GetState retrieves the state snapshot for a given height.
	GetState(ctx context.Context, height uint32) (io.ReadCloser, error)

	// GetLatestState returns the most recent state snapshot.
	GetLatestState(ctx context.Context) (height uint32, data io.ReadCloser, err error)
}

// ObjectID uniquely identifies an object within a storage provider.
type ObjectID struct {
	// Provider is the storage backend identifier
	Provider string
	// Container is the bucket/container/namespace
	Container string
	// ID is the unique object identifier within the container
	ID string
}

// String returns a URI-style representation of the object ID.
func (o ObjectID) String() string {
	return o.Provider + "://" + o.Container + "/" + o.ID
}

// ObjectInfo contains metadata about a stored object.
type ObjectInfo struct {
	ID          ObjectID
	Size        int64
	ContentType string
	Created     time.Time
	Modified    time.Time
	Checksum    string
	Attributes  map[string]string
}

// PutOptions configures object storage behavior.
type PutOptions struct {
	// ContentType specifies the MIME type of the content
	ContentType string
	// Attributes are custom key-value metadata
	Attributes map[string]string
	// Expiration sets automatic deletion time (zero means never)
	Expiration time.Time
	// Replicas specifies the minimum number of copies (provider-dependent)
	Replicas int
}

// ListOptions configures listing behavior.
type ListOptions struct {
	// MaxResults limits the number of results (0 = no limit)
	MaxResults int
	// Cursor for pagination (provider-specific)
	Cursor string
	// IncludeMetadata fetches full ObjectInfo vs just IDs
	IncludeMetadata bool
}

// Registry manages available storage providers.
type Registry struct {
	providers map[string]Provider
}

// NewRegistry creates a new provider registry.
func NewRegistry() *Registry {
	return &Registry{
		providers: make(map[string]Provider),
	}
}

// Register adds a storage provider to the registry.
func (r *Registry) Register(p Provider) {
	r.providers[p.Name()] = p
}

// Get returns a provider by name.
func (r *Registry) Get(name string) (Provider, bool) {
	p, ok := r.providers[name]
	return p, ok
}

// List returns all registered provider names.
func (r *Registry) List() []string {
	names := make([]string, 0, len(r.providers))
	for name := range r.providers {
		names = append(names, name)
	}
	return names
}

// Close closes all registered providers.
func (r *Registry) Close() error {
	var lastErr error
	for _, p := range r.providers {
		if err := p.Close(); err != nil {
			lastErr = err
		}
	}
	return lastErr
}
