package reservedcapacitytemplate

import (
	"context"
	"errors"

	"github.com/tidbcloud/pkgv2/db"
	"gorm.io/gorm"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	reservedcapacitytemplaterepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/reservedcapacitytemplate"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type repoImpl struct {
	provider db.Provider
}

func NewReservedCapacityTemplateRepo(provider db.Provider) reservedcapacitytemplaterepo.Repo {
	return &repoImpl{provider: provider}
}

func (r *repoImpl) Create(ctx context.Context, template *entity.ReservedCapacityTemplate) bizerr.BizError {
	model, err := entityToModel(template)
	if err != nil {
		return err
	}
	if err := r.provider.WriteDB(ctx).Create(model).Error; err != nil {
		return bizerr.WrapGormError(err, "create reserved capacity template")
	}
	return nil
}

func (r *repoImpl) Load(ctx context.Context, id uint64, showDeleted bool) (*entity.ReservedCapacityTemplate, bizerr.BizError) {
	if id == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "reserved capacity template id cannot be empty")
	}
	rdb := r.provider.ReadDB(ctx)
	if showDeleted {
		rdb = rdb.Unscoped()
	}
	var model schema.ReservedCapacityTemplate
	if err := rdb.Take(&model, "id = ?", id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "reserved capacity template not found")
		}
		return nil, bizerr.WrapGormError(err, "get reserved capacity template")
	}
	return modelToEntity(&model)
}

func (r *repoImpl) Query(
	ctx context.Context,
	filter *reservedcapacitytemplaterepo.Filter,
) ([]*entity.ReservedCapacityTemplate, uint32, bizerr.BizError) {
	if filter == nil {
		return nil, 0, bizerr.New(bizerr.ErrCodeParams, "filter is nil")
	}
	rdb := r.provider.ReadDB(ctx).Model(&schema.ReservedCapacityTemplate{})
	if filter.ShowDeleted {
		rdb = rdb.Unscoped()
	}
	if len(filter.Ids) > 0 {
		rdb = rdb.Where("id IN ?", filter.Ids)
	}
	if len(filter.Ids) == 0 && filter.OrganizationId != 0 {
		rdb = rdb.Where("(scope = ? OR organization_id = ?)", globalngv1.ReservedCapacityTemplate_DEFAULT.String(), filter.OrganizationId)
	} else if len(filter.Ids) == 0 {
		rdb = rdb.Where("scope = ?", globalngv1.ReservedCapacityTemplate_DEFAULT.String())
	}
	if filter.Status != globalngv1.ReservedCapacityTemplate_STATUS_UNSPECIFIED {
		rdb = rdb.Where("status = ?", filter.Status.String())
	}
	if filter.Scope != globalngv1.ReservedCapacityTemplate_SCOPE_UNSPECIFIED {
		rdb = rdb.Where("scope = ?", filter.Scope.String())
	}

	var total int64
	if err := rdb.Count(&total).Error; err != nil {
		return nil, 0, bizerr.WrapGormError(err, "count reserved capacity templates")
	}
	if filter.PageSize > 0 {
		page := filter.Page
		if page == 0 {
			page = 1
		}
		rdb = rdb.Offset(int((page - 1) * filter.PageSize)).Limit(int(filter.PageSize))
	}

	var models []schema.ReservedCapacityTemplate
	if err := rdb.Order("purchased_vcpu ASC, id ASC").Find(&models).Error; err != nil {
		return nil, 0, bizerr.WrapGormError(err, "query reserved capacity templates")
	}
	templates := make([]*entity.ReservedCapacityTemplate, 0, len(models))
	for i := range models {
		template, err := modelToEntity(&models[i])
		if err != nil {
			return nil, 0, err
		}
		templates = append(templates, template)
	}
	return templates, uint32(total), nil
}

func (r *repoImpl) UpdateStatus(
	ctx context.Context,
	id uint64,
	status globalngv1.ReservedCapacityTemplate_Status,
) (*entity.ReservedCapacityTemplate, bizerr.BizError) {
	if id == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "reserved capacity template id cannot be empty")
	}
	if err := r.provider.WriteDB(ctx).
		Model(&schema.ReservedCapacityTemplate{}).
		Where("id = ?", id).
		Update("status", status.String()).
		Error; err != nil {
		return nil, bizerr.WrapGormError(err, "update reserved capacity template status")
	}
	return r.Load(ctx, id, false)
}

func (r *repoImpl) Delete(ctx context.Context, id uint64) bizerr.BizError {
	if id == 0 {
		return bizerr.New(bizerr.ErrCodeParams, "reserved capacity template id cannot be empty")
	}
	if err := r.provider.WriteDB(ctx).Delete(&schema.ReservedCapacityTemplate{}, id).Error; err != nil {
		return bizerr.WrapGormError(err, "delete reserved capacity template")
	}
	return nil
}
