package sharedpool

import (
	"context"
	"errors"

	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/pkgv2/db"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
)

type sharedPoolObjectStoreRepoImpl struct {
	provider db.Provider
}

func NewSharedPoolObjectStoreRepo(provider db.Provider) repo.SharedPoolObjectStoreRepo {
	return &sharedPoolObjectStoreRepoImpl{
		provider: provider,
	}
}

func (r *sharedPoolObjectStoreRepoImpl) ListWithDeletedSharedPool(ctx context.Context, sharedPoolIds, storeTypes []string) ([]*entity.SharedPoolObjectStore, error) {
	rdb := r.provider.ReadDB(ctx)

	var sharedPoolObjectStores []schema.SharedPoolObjectStore

	// Join with shared_pools table to check if SharedPool is deleted
	// Always return ObjectStores whose SharedPool is deleted
	rdb = rdb.Joins("JOIN shared_pools ON shared_pools.id = shared_pool_object_stores.shared_pool_id").
		Where("shared_pools.deleted_at IS NOT NULL")

	if len(sharedPoolIds) > 0 {
		rdb = rdb.Where("shared_pool_object_stores.shared_pool_id IN ?", sharedPoolIds)
	}

	if len(storeTypes) > 0 {
		rdb = rdb.Where("shared_pool_object_stores.store_type IN ?", storeTypes)
	}

	if err := rdb.Find(&sharedPoolObjectStores).Error; err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list SharedPoolObjectStores: %v", err)
	}

	result := make([]*entity.SharedPoolObjectStore, 0, len(sharedPoolObjectStores))
	for i := range sharedPoolObjectStores {
		result = append(result, &entity.SharedPoolObjectStore{
			SharedPoolObjectStore: sharedPoolObjectStores[i],
		})
	}

	return result, nil
}

func (r *sharedPoolObjectStoreRepoImpl) Load(ctx context.Context, sharedPoolId, storeType string, deleted bool) (*entity.SharedPoolObjectStore, error) {
	rdb := r.provider.ReadDB(ctx)

	var sharedPoolObjectStore schema.SharedPoolObjectStore
	if deleted {
		rdb = rdb.Unscoped() // Load deleted records
	}

	if err := rdb.Where("shared_pool_id = ? AND store_type = ?", sharedPoolId, storeType).First(&sharedPoolObjectStore).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, status.Errorf(codes.NotFound, "SharedPoolObjectStore with sharedPoolId %s and storeType %s not found", sharedPoolId, storeType)
		}
		return nil, status.Errorf(codes.Internal, "failed to load SharedPoolObjectStore: %v", err)
	}

	return &entity.SharedPoolObjectStore{
		SharedPoolObjectStore: sharedPoolObjectStore,
	}, nil
}

func (r *sharedPoolObjectStoreRepoImpl) Delete(ctx context.Context, sharedPoolId, storeType string) error {
	return r.provider.WriteDB(ctx).Where("shared_pool_id = ? AND store_type = ?", sharedPoolId, storeType).Delete(&schema.SharedPoolObjectStore{}).Error
}
