package shard

import (
	"context"
	"fmt"

	"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
	"github.com/mr-tron/base58"
	"go.uber.org/zap"
)

// ID represents Shard identifier.
//
// Each shard should have the unique ID within
// a single instance of local storage.
type ID []byte

// NewIDFromBytes constructs ID from byte slice.
func NewIDFromBytes(v []byte) *ID {
	return (*ID)(&v)
}

func (id ID) String() string {
	return base58.Encode(id)
}

// ID returns Shard identifier.
func (s *Shard) ID() *ID {
	return s.info.ID
}

// UpdateID reads shard ID saved in the metabase and updates it if it is missing.
func (s *Shard) UpdateID(ctx context.Context) (err error) {
	var idFromMetabase []byte
	metabaseOpened := !s.GetMode().NoMetabase()
	if err = s.metaBase.Open(ctx, s.GetMode()); err != nil {
		err = fmt.Errorf("failed to open metabase: %w", err)
		metabaseOpened = false
	}
	if metabaseOpened {
		defer func() {
			cErr := s.metaBase.Close()
			if cErr != nil {
				err = fmt.Errorf("failed to close metabase: %w", cErr)
			}
		}()
		if idFromMetabase, err = s.metaBase.ReadShardID(); err != nil {
			err = fmt.Errorf("failed to read shard id from metabase: %w", err)
		}
	}

	if len(idFromMetabase) != 0 {
		s.info.ID = NewIDFromBytes(idFromMetabase)
	}

	shardID := s.info.ID.String()
	if s.cfg.metricsWriter != nil {
		s.cfg.metricsWriter.SetShardID(shardID)
	}
	if s.writeCache != nil && s.writeCache.GetMetrics() != nil {
		s.writeCache.GetMetrics().SetShardID(shardID)
	}

	s.log = &logger.Logger{Logger: s.log.With(zap.Stringer("shard_id", s.info.ID))}
	s.metaBase.SetLogger(s.log)
	s.blobStor.SetLogger(s.log)
	if s.hasWriteCache() {
		s.writeCache.SetLogger(s.log)
	}
	s.metaBase.SetParentID(s.info.ID.String())
	s.blobStor.SetParentID(s.info.ID.String())
	if s.pilorama != nil {
		s.pilorama.SetParentID(s.info.ID.String())
	}

	if len(idFromMetabase) == 0 && metabaseOpened {
		if err = s.metaBase.WriteShardID(*s.info.ID); err != nil {
			err = fmt.Errorf("failed to write shard id to metabase: %w", err)
		}
	}
	return
}