package counter

import (
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"github.com/Luzifer/twitch-bot/v3/internal/helpers"
	"github.com/Luzifer/twitch-bot/v3/pkg/database"
)

type (
	Counter struct {
		Name  string `gorm:"primaryKey"`
		Value int64
	}
)

func GetCounterValue(db database.Connector, counterName string) (int64, error) {
	var c Counter

	err := helpers.Retry(func() error {
		err := db.DB().First(&c, "name = ?", counterName).Error
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil
		}

		return err
	})

	return c.Value, errors.Wrap(err, "querying counter")
}

func UpdateCounter(db database.Connector, counterName string, value int64, absolute bool) error {
	if !absolute {
		cv, err := GetCounterValue(db, counterName)
		if err != nil {
			return errors.Wrap(err, "getting previous value")
		}

		value += cv
	}

	return errors.Wrap(
		helpers.RetryTransaction(db.DB(), func(tx *gorm.DB) error {
			return tx.Clauses(clause.OnConflict{
				Columns:   []clause.Column{{Name: "name"}},
				DoUpdates: clause.AssignmentColumns([]string{"value"}),
			}).Create(Counter{Name: counterName, Value: value}).Error
		}),
		"storing counter value",
	)
}

func getCounterRank(db database.Connector, prefix, name string) (rank, count int64, err error) {
	var cc []Counter

	if err = helpers.Retry(func() error {
		return db.DB().
			Order("value DESC").
			Find(&cc, "name LIKE ?", prefix+"%").
			Error
	}); err != nil {
		return 0, 0, errors.Wrap(err, "querying counters")
	}

	for i, c := range cc {
		count++
		if c.Name == name {
			rank = int64(i + 1)
		}
	}

	return rank, count, nil
}

func getCounterTopList(db database.Connector, prefix string, n int) ([]Counter, error) {
	var cc []Counter

	err := helpers.Retry(func() error {
		return db.DB().
			Order("value DESC").
			Limit(n).
			Find(&cc, "name LIKE ?", prefix+"%").
			Error
	})

	return cc, errors.Wrap(err, "querying counters")
}