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

import (
	"context"

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

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

type (
	PatchApproval func(current *apprent.Approval) error
)

type ApprovalRepo interface {
	ListApprovals(ctx context.Context, opts ...repoopt.Option) ([]*apprent.Approval, error)
	GetApproval(ctx context.Context, id uint64) (*apprent.Approval, error)
	CreateApproval(ctx context.Context, approval *apprent.Approval) (*apprent.Approval, error)
	UpdateApproval(ctx context.Context, approval *apprent.Approval) (*apprent.Approval, error)
	PatchApproval(ctx context.Context, current *apprent.Approval, patch ...PatchApproval) (*apprent.Approval, error)
	CountApproval(ctx context.Context, opts ...repoopt.Option) (int64, error)
}

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

	return &repo{
		driver: driver,
	}
}

type repo struct {
	driver Driver
}

func (r *repo) ListApprovals(ctx context.Context, opts ...repoopt.Option) ([]*apprent.Approval, error) {
	return r.driver.Query(ctx, opts...)
}

func (r *repo) GetApproval(ctx context.Context, id uint64) (*apprent.Approval, error) {
	return r.driver.Load(ctx, id)
}

func (r *repo) CreateApproval(ctx context.Context, approval *apprent.Approval) (*apprent.Approval, error) {
	err := validation.Approval.Approval(approval)
	if err != nil {
		return nil, err
	}

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

func (r *repo) UpdateApproval(ctx context.Context, approval *apprent.Approval) (*apprent.Approval, error) {
	err := validation.Approval.Approval(approval)
	if err != nil {
		return nil, err
	}

	return r.driver.Save(ctx, approval)

}

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

	err := validation.Approval.Approval(current)
	if err != nil {
		return nil, err
	}
	return r.driver.Save(ctx, current)
}

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