package replay

import (
	"context"

	"github.com/tidbcloud/dataflow-service/app/repo"
	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/db"
	"gorm.io/gorm"
)

type replayRepoImpl struct {
	dbProvider db.Provider
}

var _ repo.ReplayRepo = (*replayRepoImpl)(nil)

func NewReplayRepo(dbProvider db.Provider) repo.ReplayRepo {
	return &replayRepoImpl{dbProvider: dbProvider}
}

func (r *replayRepoImpl) Load(ctx context.Context, id uint64) (*entity.Replay, bizerr.BizError) {
	rdb := r.dbProvider.ReadDB(ctx)
	replay := &entity.Replay{}
	err := entity.NewReplayQuerySet(rdb).
		IDEq(entity.ID(id)).
		One(replay)
	if err != nil {
		return nil, bizerr.WrapGormError(err, "load replay, id=%d", id)
	}
	return replay, nil
}

func (r *replayRepoImpl) Save(ctx context.Context, replay *entity.Replay) bizerr.BizError {
	wdb := r.dbProvider.WriteDB(ctx)
	if err := entity.SaveWithVersioning(wdb, replay); err != nil {
		return err.Chain("save replay, id=%d", replay.ID)
	}
	return nil
}

func (r *replayRepoImpl) Create(ctx context.Context, replay *entity.Replay) bizerr.BizError {
	wdb := r.dbProvider.WriteDB(ctx)
	if err := wdb.Create(replay).Error; err != nil {
		return bizerr.WrapGormError(err, "create replay")
	}
	return nil
}

func (r *replayRepoImpl) List(
	ctx context.Context, filterOps ...repo.ReplayFilterOp,
) ([]entity.Replay, bizerr.BizError) {
	rdb := r.dbProvider.ReadDB(ctx)
	qs := entity.NewReplayQuerySet(rdb)
	for _, op := range filterOps {
		qs = op(qs)
	}
	var replays []entity.Replay
	if err := qs.All(&replays); err != nil {
		return nil, bizerr.WrapGormError(err, "list replays")
	}
	return replays, nil
}

func (r *replayRepoImpl) ListWithPager(
	ctx context.Context, filterOp repo.ReplayFilterOp, pagerOp repo.ReplayPagerOp,
) ([]entity.Replay, bizerr.BizError) {
	rdb := r.dbProvider.ReadDB(ctx)
	qs := entity.NewReplayQuerySet(rdb)
	if filterOp != nil {
		qs = filterOp(qs)
	}
	if pagerOp != nil {
		qs = pagerOp(qs)
	}
	var replays []entity.Replay
	if err := qs.All(&replays); err != nil {
		return nil, bizerr.WrapGormError(err, "list replays with pager")
	}
	return replays, nil
}

func (r *replayRepoImpl) Count(ctx context.Context, filterOps ...repo.ReplayFilterOp) (int64, bizerr.BizError) {
	rdb := r.dbProvider.ReadDB(ctx)
	qs := entity.NewReplayQuerySet(rdb)
	for _, op := range filterOps {
		qs = op(qs)
	}
	count, err := qs.Count()
	if err != nil {
		return 0, bizerr.WrapGormError(err, "count replays")
	}
	return count, nil
}

func (r *replayRepoImpl) Delete(ctx context.Context, id uint64) bizerr.BizError {
	wdb := r.dbProvider.WriteDB(ctx)
	replay := &entity.Replay{}
	replay.ID = entity.ID(id)
	if err := wdb.Delete(replay).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil
		}
		return bizerr.WrapGormError(err, "delete replay, id=%d", id)
	}
	return nil
}
