package shard

import (
	"context"
	"fmt"

	"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
)

type ContainerSizePrm struct {
	cnr cid.ID
}

type ContainerSizeRes struct {
	size uint64
}

func (p *ContainerSizePrm) SetContainerID(cnr cid.ID) {
	p.cnr = cnr
}

func (r ContainerSizeRes) Size() uint64 {
	return r.size
}

func (s *Shard) ContainerSize(prm ContainerSizePrm) (ContainerSizeRes, error) {
	s.m.RLock()
	defer s.m.RUnlock()

	if s.info.Mode.NoMetabase() {
		return ContainerSizeRes{}, ErrDegradedMode
	}

	size, err := s.metaBase.ContainerSize(prm.cnr)
	if err != nil {
		return ContainerSizeRes{}, fmt.Errorf("could not get container size: %w", err)
	}

	return ContainerSizeRes{
		size: size,
	}, nil
}

type ContainerCountPrm struct {
	ContainerID cid.ID
}

type ContainerCountRes struct {
	Phy   uint64
	Logic uint64
	User  uint64
}

func (s *Shard) ContainerCount(ctx context.Context, prm ContainerCountPrm) (ContainerCountRes, error) {
	ctx, span := tracing.StartSpanFromContext(ctx, "Shard.ContainerCount",
		trace.WithAttributes(
			attribute.String("shard_id", s.ID().String()),
			attribute.Stringer("container_id", prm.ContainerID),
		))
	defer span.End()

	s.m.RLock()
	defer s.m.RUnlock()

	if s.info.Mode.NoMetabase() {
		return ContainerCountRes{}, ErrDegradedMode
	}

	counters, err := s.metaBase.ContainerCount(ctx, prm.ContainerID)
	if err != nil {
		return ContainerCountRes{}, fmt.Errorf("could not get container counters: %w", err)
	}

	return ContainerCountRes{
		Phy:   counters.Phy,
		Logic: counters.Logic,
		User:  counters.User,
	}, nil
}

func (s *Shard) DeleteContainerSize(ctx context.Context, id cid.ID) error {
	ctx, span := tracing.StartSpanFromContext(ctx, "Shard.DeleteContainerSize",
		trace.WithAttributes(
			attribute.String("shard_id", s.ID().String()),
			attribute.Stringer("container_id", id),
		))
	defer span.End()

	s.m.RLock()
	defer s.m.RUnlock()

	if s.info.Mode.ReadOnly() {
		return ErrReadOnlyMode
	}

	if s.info.Mode.NoMetabase() {
		return ErrDegradedMode
	}

	return s.metaBase.DeleteContainerSize(ctx, id)
}

func (s *Shard) DeleteContainerCount(ctx context.Context, id cid.ID) error {
	ctx, span := tracing.StartSpanFromContext(ctx, "Shard.DeleteContainerCount",
		trace.WithAttributes(
			attribute.String("shard_id", s.ID().String()),
			attribute.Stringer("container_id", id),
		))
	defer span.End()

	s.m.RLock()
	defer s.m.RUnlock()

	if s.info.Mode.ReadOnly() {
		return ErrReadOnlyMode
	}

	if s.info.Mode.NoMetabase() {
		return ErrDegradedMode
	}

	return s.metaBase.DeleteContainerCount(ctx, id)
}