package repo

import (
	"context"

	relent "github.com/tidbcloud/devops/entity/release"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
)

type MockChangeDriver struct {
	Changes map[uint64]*relent.ChangeInRepo

	LoadFunc  func(ctx context.Context, id uint64, detail bool) (*relent.ChangeInRepo, error)
	SaveFunc  func(ctx context.Context, change *relent.ChangeInRepo) (*relent.ChangeInRepo, error)
	QueryFunc func(ctx context.Context, detail bool, opt ...repoopt.Option) ([]*relent.ChangeInRepo, error)
	CountFunc func(ctx context.Context, opts ...repoopt.Option) (int64, error)
}

func NewMockChangeDriver() *MockChangeDriver {
	return &MockChangeDriver{
		Changes: make(map[uint64]*relent.ChangeInRepo),
	}
}

func (r *MockChangeDriver) Load(ctx context.Context, id uint64, detail bool) (*relent.ChangeInRepo, error) {
	if r.LoadFunc != nil {
		return r.LoadFunc(ctx, id, detail)
	}

	change, err := pointer.DeepCopy(r.Changes[id])
	if err != nil {
		return nil, err
	}

	return change, nil
}

func (r *MockChangeDriver) Save(ctx context.Context, change *relent.ChangeInRepo) (*relent.ChangeInRepo, error) {
	if r.SaveFunc != nil {
		return r.SaveFunc(ctx, change)
	}

	savedChange, err := pointer.DeepCopy(change)
	if err != nil {
		return nil, err
	}

	r.Changes[change.ID] = savedChange

	reChange, err := pointer.DeepCopy(savedChange)
	if err != nil {
		return nil, err
	}

	return reChange, nil
}

func (r *MockChangeDriver) Query(ctx context.Context, detail bool, opt ...repoopt.Option) ([]*relent.ChangeInRepo, error) {
	if r.QueryFunc != nil {
		return r.QueryFunc(ctx, detail, opt...)
	}

	result := make([]*relent.ChangeInRepo, 0, len(r.Changes))
	for _, change := range r.Changes {
		reChange, err := pointer.DeepCopy(change)
		if err != nil {
			return nil, err
		}
		result = append(result, reChange)
	}
	return result, nil
}

func (r *MockChangeDriver) Count(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	if r.CountFunc != nil {
		return r.CountFunc(ctx, opts...)
	}

	return int64(len(r.Changes)), nil
}
