//go:generate ${GOBIN}/mockgen -destination repo_mock.go -package=maintenance ${GO_MODULE}/pkg/service/clustermaintenance/repo/maintenance MaintenanceRepo
package maintenance

import (
	"context"
	"fmt"

	maintent "github.com/tidbcloud/devops/entity/maintenance"

	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
	"github.com/tidbcloud/devops/pkg/service/clustermaintenance/repo/cvt"

	"github.com/tidbcloud/pkgv2/db"
)

type Patch func(current *maintent.ClusterMaintenanceInRepo) error

type MaintenanceRepo interface {
	List(ctx context.Context, opts ...repoopt.Option) ([]*maintent.ClusterMaintenanceInRepo, error)
	Get(ctx context.Context, id uint64) (*maintent.ClusterMaintenanceInRepo, error)
	Create(ctx context.Context, spec *maintent.ClusterMaintenanceInRepo) (*maintent.ClusterMaintenanceInRepo, error)
	Count(ctx context.Context, opts ...repoopt.Option) (int64, error)
	Patch(ctx context.Context, current *maintent.ClusterMaintenanceInRepo, patches ...Patch) error
	Options() opts
}

func NewMaintenanceRepo(dbp db.Provider) MaintenanceRepo {
	return &repo{
		driver: &driver{
			cvter:      cvt.NewConverter(),
			dbProvider: dbp,
		},
	}
}

type repo struct {
	driver Driver
}

func (r *repo) List(ctx context.Context, opts ...repoopt.Option) ([]*maintent.ClusterMaintenanceInRepo, error) {
	return r.driver.Query(ctx, opts...)
}

func (r *repo) Get(ctx context.Context, id uint64) (*maintent.ClusterMaintenanceInRepo, error) {
	return r.driver.Load(ctx, id)
}

func (r *repo) Create(ctx context.Context, spec *maintent.ClusterMaintenanceInRepo) (*maintent.ClusterMaintenanceInRepo, error) {
	if spec.ID != 0 {
		return nil, fmt.Errorf("id must be 0")
	}

	return r.driver.Save(ctx, spec)
}

func (r *repo) Count(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	return r.driver.Count(ctx, opts...)
}

func (r *repo) Patch(ctx context.Context, current *maintent.ClusterMaintenanceInRepo, patches ...Patch) error {
	if current.ID == 0 {
		return fmt.Errorf("id must not be 0")
	}

	for _, patch := range patches {
		err := patch(current)
		if err != nil {
			return err
		}
	}

	_, err := r.driver.Save(ctx, current)
	return err
}

func (r *repo) Options() opts {
	return opts{}
}
