// Package repoext provides extended repository functionality for keyspace management.
package repoext

import (
	"context"
	"fmt"

	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type KeyspaceFetcher interface {
	GetKeyspace(ctx context.Context, clusterId, sharedPoolId string) (string, error)
	GetClusterIds(ctx context.Context, sharedPoolId string, keyspaces []string) (map[string]string /* keyspace to cluster id */, error)
}

type keyspaceFetcher struct {
	tidbGroupRepo repo.TiDBGroupRepo
}

func NewKeyspaceFetcher(tidbGroupRepo repo.TiDBGroupRepo) KeyspaceFetcher {
	return &keyspaceFetcher{tidbGroupRepo: tidbGroupRepo}
}

var (
	TiDBGroupNotFoundErr = fmt.Errorf("tidb group not found")
	DifferentKeyspaceErr = fmt.Errorf("multiple tidb groups for a cluster with different keyspaces")
)

func (t *keyspaceFetcher) GetKeyspace(ctx context.Context, clusterId, sharedPoolId string) (string, error) {
	items, err := t.tidbGroupRepo.List(ctx, []string{sharedPoolId}, nil, []string{clusterId}, nil, true)
	if err != nil {
		tilog.Error(ctx, "list TidbGroup entity failed", zap.Error(err), zap.String("sharedPoolId", sharedPoolId), zap.String("clusterId", clusterId))
		return "", fmt.Errorf("failed to list TidbGroup: %w", err)
	}

	if len(items) == 0 {
		tilog.Error(ctx, "tidb group not found for when get keyspace by cluster id", zap.String("clusterId", clusterId), zap.String("sharedPoolId", sharedPoolId))
		return "", TiDBGroupNotFoundErr
	}

	keyspace := items[0].ClusterId
	if len(items) > 1 {
		for i := 1; i < len(items); i++ {
			if items[i].ClusterId != keyspace {
				tilog.Error(ctx, "multiple tidb groups for a cluster with different keyspaces",
					zap.String("clusterId", clusterId), zap.String("sharedPoolId", sharedPoolId), zap.String("tidbGroups", utils.JSON(items)))
				return "", DifferentKeyspaceErr
			}
		}
	}

	return keyspace, nil
}

func (t *keyspaceFetcher) GetClusterIds(ctx context.Context, sharedPoolId string, keyspaces []string) (map[string]string, error) {
	items, err := t.tidbGroupRepo.List(ctx, []string{sharedPoolId}, nil, nil, keyspaces, true)
	if err != nil {
		tilog.Error(ctx, "list TidbGroup entity failed", zap.Error(err), zap.String("sharedPoolId", sharedPoolId), zap.Any("keyspaces", keyspaces))
		return nil, fmt.Errorf("failed to list TidbGroup: %w", err)
	}

	if len(items) == 0 {
		tilog.Error(ctx, "tidb group not found when get cluster ids by keyspaces", zap.String("sharedPoolId", sharedPoolId), zap.Any("keyspaces", keyspaces))
		return nil, TiDBGroupNotFoundErr
	}

	k2c := map[string]string{}
	for _, it := range items {
		k2c[it.ClusterId] = it.ClusterId
	}
	return k2c, nil
}
