package syncer

import (
	"context"
	"fmt"
	"strings"

	"github.com/sirupsen/logrus"
	"go.uber.org/multierr"

	"github.com/tidbcloud/observability/backup-worker/pkg/client"
	"github.com/tidbcloud/observability/backup-worker/pkg/configs"
)

type CopyFunc func(ctx context.Context, srcBucket, srcKey, destKey string, size int64) error

type baseSyncer struct {
	provider       string
	srcBucket      string
	srcCli         client.Client
	destCli        client.Client
	copyFunc       CopyFunc
	destPathPrefix string
}

func (s *baseSyncer) Type() string {
	return s.provider
}

func (s *baseSyncer) Sync(ctx context.Context, shard configs.FileShard) (int64, error) {
	objects, err := s.srcCli.ListObjects(ctx, shard.SourcePrefix)
	if err != nil {
		return 0, fmt.Errorf("list %s objects for shard %s: %w", s.provider, shard.ID(), err)
	}

	if len(objects) == 0 {
		logrus.Infof("No %s objects found for shard %s", s.provider, shard.ID())
		return 0, nil
	}
	logrus.Infof("Found %d %s objects for shard %s", len(objects), s.provider, shard.ID())

	var errs error
	var totalSize int64
	for _, obj := range objects {
		destKey := shard.DestPrefix + strings.TrimPrefix(obj.Name, shard.SourcePrefix)

		// Check if the object already exists at the destination.
		// CheckObject normalizes the key by prepending destPathPrefix+"/",
		// so we strip that prefix to produce the correct raw key.
		checkKey := strings.TrimPrefix(destKey, s.destPathPrefix+"/")
		exists, err := s.destCli.CheckObject(ctx, checkKey)
		if err != nil {
			errs = multierr.Append(errs, fmt.Errorf("check %s object %s: %w", s.provider, destKey, err))
			continue
		}
		if exists {
			logrus.Infof("Skip existing %s object: %s", s.provider, destKey)
			totalSize += obj.Size
			continue
		}

		err = s.copyFunc(ctx, s.srcBucket, obj.Name, destKey, obj.Size)
		if err != nil {
			errs = multierr.Append(errs, fmt.Errorf("copy %s object %s -> %s: %w", s.provider, obj.Name, destKey, err))
			continue
		}
		totalSize += obj.Size
		logrus.Infof("Copied %s object: %s/%s -> %s, size=%d", s.provider, s.srcBucket, obj.Name, destKey, obj.Size)
	}
	return totalSize, errs
}
