package s3local

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
	"git.frostfs.info/TrueCloudLab/xk6-frostfs/internal/local/rawclient"
)

// frostfs implements the subset of layer.FrostFS needed for clients
// backed by local storage engines. Attempting to call any of the
// unimplemented methods panics.
type frostfs struct {
	*rawclient.RawClient
}

func unimplementedMessage(fname string) string {
	return fmt.Sprintf("layer.FrostFS.%s is unimplemented and should not be called. If you are seeing "+
		"this error, it probably means you tried to use the s3local scenario for "+
		"something other than filling a cluster (i.e. PUT or GET).", fname)
}

func (*frostfs) CreateContainer(context.Context, layer.PrmContainerCreate) (*layer.ContainerCreateResult, error) {
	panic(unimplementedMessage("CreateContainer"))
}

func (*frostfs) Container(ctx context.Context, prmContainer layer.PrmContainer) (*container.Container, error) {
	panic(unimplementedMessage("Container"))
}

func (*frostfs) UserContainers(ctx context.Context, containers layer.PrmUserContainers) ([]cid.ID, error) {
	panic(unimplementedMessage("UserContainers"))
}

func (*frostfs) SetContainerEACL(context.Context, eacl.Table, *session.Container) error {
	panic(unimplementedMessage("SetContainerEACL"))
}

func (*frostfs) ContainerEACL(ctx context.Context, containerEACL layer.PrmContainerEACL) (*eacl.Table, error) {
	panic(unimplementedMessage("ContainerEACL"))
}

func (*frostfs) DeleteContainer(context.Context, cid.ID, *session.Container) error {
	panic(unimplementedMessage("DeleteContainer"))
}

func (f *frostfs) ReadObject(ctx context.Context, prm layer.PrmObjectRead) (*layer.ObjectPart, error) {
	obj, err := f.Get(ctx, prm.Container, prm.Object)
	if err != nil {
		return nil, err
	}
	part := &layer.ObjectPart{}
	if prm.WithHeader {
		part.Head = obj
	}
	if prm.WithPayload {
		part.Payload = io.NopCloser(bytes.NewReader(obj.Payload()))
	}
	return part, nil
}

func (f *frostfs) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (oid.ID, error) {
	payload, err := io.ReadAll(prm.Payload)
	if err != nil {
		return oid.ID{}, fmt.Errorf("reading payload: %v", err)
	}
	hdrs := map[string]string{}
	for _, attr := range prm.Attributes {
		hdrs[attr[0]] = attr[1]
	}
	return f.Put(ctx, prm.Container, nil, hdrs, payload)
}

func (f *frostfs) DeleteObject(context.Context, layer.PrmObjectDelete) error {
	panic(unimplementedMessage("DeleteObject"))
}

func (f *frostfs) TimeToEpoch(ctx context.Context, now time.Time, future time.Time) (uint64, uint64, error) {
	panic(unimplementedMessage("TimeToEpoch"))
}

func (f *frostfs) SearchObjects(ctx context.Context, search layer.PrmObjectSearch) ([]oid.ID, error) {
	panic(unimplementedMessage("SearchObjects"))
}