//go:generate ${GOBIN}/mockgen -destination repo_mock.go -package=repo ${GO_MODULE}/pkg/service/operation/repo OperationRepo
package repo

import (
	"context"

	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/entity/validation"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
	"github.com/tidbcloud/devops/pkg/service/operation/repo/cvt"

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

type (
	PatchInstance func(current *opent.Instance) error
)

type OperationRepo interface {
	// Instances
	ListInstances(ctx context.Context, opts ...repoopt.Option) ([]*opent.Instance, error)
	GetInstance(ctx context.Context, id uint64) (*opent.Instance, error)
	CreateInstance(ctx context.Context, instance *opent.Instance) (*opent.Instance, error)
	PatchInstance(ctx context.Context, current *opent.Instance, patch ...PatchInstance) error
	CountInstance(ctx context.Context, opts ...repoopt.Option) (int64, error)
}

func NewOperationRepo(dbp db.Provider) OperationRepo {
	driver := &driver{
		cvter:      cvt.NewConverter(),
		dbProvider: dbp,
	}

	return &repo{
		insDrvier: (*instanceDriver)(driver),
	}
}

type driver struct {
	cvter      *cvt.Converter
	dbProvider db.Provider
}

type repo struct {
	insDrvier InstanceDriver
}

func (r *repo) ListInstances(ctx context.Context, opts ...repoopt.Option) ([]*opent.Instance, error) {
	return r.insDrvier.Query(ctx, opts...)
}

func (r *repo) GetInstance(ctx context.Context, id uint64) (*opent.Instance, error) {
	return r.insDrvier.Load(ctx, id)
}

func (r *repo) CreateInstance(ctx context.Context, instance *opent.Instance) (*opent.Instance, error) {
	// validate
	err := validation.Operation.Instance(instance)
	if err != nil {
		return nil, err
	}

	return r.insDrvier.Save(ctx, instance)
}

func (r *repo) PatchInstance(ctx context.Context, current *opent.Instance, patch ...PatchInstance) error {
	for _, p := range patch {
		err := p(current)
		if err != nil {
			return err
		}
	}

	err := validation.Operation.Instance(current)
	if err != nil {
		return err
	}

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

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