package tidbgroup

import (
	"context"

	"github.com/tidbcloud/pkgv2/db"
	"go.opentelemetry.io/otel"
	"gorm.io/gorm"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	tidbgrouprepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/tidbgroup"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type TidbGroup struct {
	gorm.Model

	DisplayName string `gorm:"type:VARCHAR(255);not null"`
	Default     bool   `gorm:"not null"`
	ClusterId   uint64 `gorm:"index"`
	// MinRcu      int64  `gorm:"default:0"` // remove to cluster
	// MaxRcu      int64  `gorm:"default:0"` // remove to cluster
}

var tracer = otel.Tracer("tidbgroup_repo_impl")

func NewTidbGroupRepo(provider db.Provider) tidbgrouprepo.Repo {
	return &tidbgroupRepoImpl{
		provider: provider,
	}
}

type tidbgroupRepoImpl struct {
	provider db.Provider
}

func (i *tidbgroupRepoImpl) Create(ctx context.Context, tidbGroup *entity.TidbGroup) bizerr.BizError {
	ctx, span := tracer.Start(ctx, "tidbgroupRepo.Create")
	defer span.End()

	wdb := i.provider.WriteDB(ctx)

	schemaTidbGroup := enToModel(tidbGroup)
	err := wdb.Save(schemaTidbGroup).Error
	if err != nil {
		return bizerr.WrapGormError(err, "failed to create tidbgroup")
	}

	return nil
}

func (i *tidbgroupRepoImpl) Save(ctx context.Context, tidbGroup *entity.TidbGroup) bizerr.BizError {
	ctx, span := tracer.Start(ctx, "tidbgroupRepo.Save")
	defer span.End()

	wdb := i.provider.WriteDB(ctx)

	schemaTidbGroup := enToModel(tidbGroup)
	err := wdb.Omit("created_at").Save(schemaTidbGroup).Error
	if err != nil {
		return bizerr.WrapGormError(err, "save tidbgroup")
	}

	return nil
}

func (i *tidbgroupRepoImpl) Load(ctx context.Context, clusterID, id uint64) (*entity.TidbGroup, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "tidbgroupRepo.Load")
	defer span.End()

	var schemaTidbGroup schema.TidbGroup
	rdb := i.provider.ReadDB(ctx)
	if err := rdb.Model(&schema.TidbGroup{}).Where("`cluster_id` = ? AND `id` = ?", clusterID, id).First(&schemaTidbGroup).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "load tidb node group")
	}

	return modelToEn(&schemaTidbGroup), nil
}

func (i *tidbgroupRepoImpl) Delete(ctx context.Context, tidbGroup *entity.TidbGroup) bizerr.BizError {
	ctx, span := tracer.Start(ctx, "tidbgroupRepo.Delete")
	defer span.End()

	wdb := i.provider.WriteDB(ctx)

	if err := wdb.Where("`id` = ? and cluster_id = ?", tidbGroup.Id, tidbGroup.ClusterId).Delete(&schema.TidbGroup{}).Error; err != nil {
		return bizerr.WrapGormError(err, "delete work pool")
	}
	return nil
}

func (i *tidbgroupRepoImpl) DeleteByClusterId(ctx context.Context, clusterID uint64) bizerr.BizError {
	ctx, span := tracer.Start(ctx, "tidbgroupRepo.DeleteByClusterId")
	defer span.End()

	wdb := i.provider.WriteDB(ctx)

	if err := wdb.Where("`cluster_id` = ?", clusterID).Delete(&schema.TidbGroup{}).Error; err != nil {
		return bizerr.WrapGormError(err, "delete tidb group by cluster id")
	}
	return nil
}

func (i *tidbgroupRepoImpl) Query(
	ctx context.Context,
	filter *tidbgrouprepo.Filter,
) ([]*entity.TidbGroup, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "tidbgroupRepo.Query")
	defer span.End()

	rdb := i.provider.ReadDB(ctx)

	var schemaTidbGroups []*schema.TidbGroup
	tx := rdb.Model(&schema.TidbGroup{})

	if filter.ShowDeleted {
		tx = tx.Unscoped()
	}
	if len(filter.Ids) > 0 {
		tx = tx.Where("`id` IN ?", filter.Ids)
	}
	if len(filter.ClusterIds) > 0 {
		tx = tx.Where("`cluster_id` IN ?", filter.ClusterIds)
	}
	if len(filter.DisplayNames) > 0 {
		tx = tx.Where("`display_name` IN ?", filter.DisplayNames)
	}
	if filter.Default != nil {
		tx = tx.Where("`default` = ?", filter.Default)
	}
	if err := tx.Find(&schemaTidbGroups).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "query tidb group")
	}

	var tidbGroups []*entity.TidbGroup
	for _, schemaTidbGroup := range schemaTidbGroups {
		tidbGroups = append(tidbGroups, modelToEn(schemaTidbGroup))
	}

	return tidbGroups, nil
}
