package spu

import (
	"context"

	"github.com/tidbcloud/pkgv2/db"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/spu"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/infra/repoimpl/spu/cvt"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type storageMuRepoImpl struct {
	provider db.Provider
}

func NewStorageMuRepo(provider db.Provider) spu.StorageMuRepo {
	return &storageMuRepoImpl{
		provider: provider,
	}
}

func (it *storageMuRepoImpl) Save(ctx context.Context, en *entity.StorageMu) (uint, bizerr.BizError) {
	// Query provider first to get their IDs.
	var providerModel schema.Provider
	if err := it.provider.WriteDB(ctx).Take(&providerModel, "name = ?", en.CloudProvider.String()).Error; err != nil {
		return 0, bizerr.WrapGormError(err, "load provider failed")
	}

	storageTemplate, err := cvt.StorageTemplateEnToModel(en, uint64(providerModel.ID))
	if err != nil {
		return 0, bizerr.WrapError(err, "convert instance type failed")
	}

	wdb := it.provider.WriteDB(ctx)
	if storageTemplate.ID != 0 {
		wdb = wdb.Omit("created_at")
	}
	if err := wdb.Save(storageTemplate).Error; err != nil {
		return 0, bizerr.WrapGormError(err, "create instance type failed")
	}

	return storageTemplate.ID, nil
}

func (it *storageMuRepoImpl) List(
	ctx context.Context,
	filter spu.StorageMuFilter,
) ([]*entity.StorageMu, bizerr.BizError) {
	rdb := it.provider.ReadDB(ctx).Model(&schema.StorageTemplate{})

	if filter.Provider != globalcommonv1.CloudProvider_NAME_UNSPECIFIED {
		rdb = rdb.Where("`Provider`.`name` = ?", filter.Provider.String())
	}
	if len(filter.VolumeTypes) > 0 {
		rdb = rdb.Where("`volume_type` IN (?)", filter.VolumeTypes)
	}

	var its []*schema.StorageTemplate
	if err := rdb.InnerJoins("Provider").Find(&its).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "load storage templates")
	}

	out := make([]*entity.StorageMu, 0, len(its))
	for _, it := range its {
		en, err := cvt.StorageTemplateModelToEn(it, &it.Provider)
		if err != nil {
			return nil, bizerr.WrapError(err, "convert storage template %d failed", it.ID)
		}
		out = append(out, en)
	}

	return out, nil
}

func (it *storageMuRepoImpl) Get(ctx context.Context, id uint64) (*entity.StorageMu, bizerr.BizError) {
	var st schema.StorageTemplate
	if err := it.provider.ReadDB(ctx).Take(&st, id).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "load storage template %d", id)
	}

	provider := schema.Provider{}
	if err := it.provider.ReadDB(ctx).Take(&provider, st.ProviderId).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "load provider %d", st.ProviderId)
	}

	en, err := cvt.StorageTemplateModelToEn(&st, &provider)
	if err != nil {
		return nil, bizerr.WrapError(err, "convert storage template %d failed", id)
	}

	return en, nil
}

func (it *storageMuRepoImpl) Delete(ctx context.Context, id uint64) bizerr.BizError {
	if err := it.provider.WriteDB(ctx).Delete(&schema.StorageTemplate{}, id).Error; err != nil {
		return bizerr.WrapGormError(err, "delete storage template %d", id)
	}
	return nil
}
