package client

import (
	"context"
	"fmt"

	"github.com/tidbcloud/observability/backup-worker/pkg/client/azblob"
	"github.com/tidbcloud/observability/backup-worker/pkg/client/gcs"
	"github.com/tidbcloud/observability/backup-worker/pkg/client/oss"
	"github.com/tidbcloud/observability/backup-worker/pkg/client/s3"
	"github.com/tidbcloud/observability/backup-worker/pkg/common"
	"github.com/tidbcloud/observability/backup-worker/pkg/configs"
)

// Client defines the contract for backup storage backends.
//
// The interface uses a two-phase approach (check then sink) to enable
// idempotent backups without redundant data transfer.
type Client interface {
	// Type returns the backend identifier for logging and metrics.
	Type() string

	// CheckObject verifies if an object exists at the given key.
	CheckObject(ctx context.Context, key string) (bool, error)

	// SinkObject uploads a local file to object storage.
	SinkObject(ctx context.Context, key, localPath string) error

	// ListObjects iterates through all objects under the given prefix.
	ListObjects(ctx context.Context, prefix string) ([]common.ObjectInfo, error)

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

// New creates a Client based on the configuration.
//
// The first enabled sink wins, enforcing single-sink configuration.
// This factory pattern allows adding new backends without modifying
// the controller or other sink implementations.
func New(ctx context.Context, cfg configs.Config) (Client, error) {
	if cfg.Sinks.AzBlob.Enabled {
		return azblob.New(ctx, cfg.Sinks.AzBlob, cfg.RetryPolicy)
	}
	if cfg.Sinks.Gcs.Enabled {
		return gcs.New(ctx, cfg.Sinks.Gcs, cfg.RetryPolicy)
	}
	if cfg.Sinks.Oss.Enabled {
		return oss.New(ctx, cfg.Sinks.Oss, cfg.RetryPolicy)
	}
	if cfg.Sinks.S3.Enabled {
		return s3.New(ctx, cfg.Sinks.S3, cfg.RetryPolicy)
	}
	return nil, fmt.Errorf("no sinks enabled")
}
