package levelrepo

import (
	"context"
	"database/sql"
)

type Level struct {
	GuildID int64
	UserID  int64
	Level   int
	XP      int64
}

type Repo struct {
	db *sql.DB
}

func NewRepo(db *sql.DB) *Repo {
	return &Repo{db: db}
}

func (r *Repo) GetLevel(ctx context.Context, guildID, userID int64) (*Level, error) {
	const q = `
		SELECT level, xp
		FROM levels
		WHERE guild = $1 AND user_id = $2
	`
	row := r.db.QueryRowContext(ctx, q, guildID, userID)
	var lvl Level
	lvl.GuildID = guildID
	lvl.UserID = userID
	switch err := row.Scan(&lvl.Level, &lvl.XP); err {
	case sql.ErrNoRows:
		// default new user
		lvl.Level = 0
		lvl.XP = 0
		return &lvl, nil
	case nil:
		return &lvl, nil
	default:
		return nil, err
	}
}
func (r *Repo) UpsertLevel(ctx context.Context, lvl *Level) error {
	const q = `
		INSERT INTO levels (guild, user_id, level, xp)
		VALUES ($1, $2, $3, $4)
		ON CONFLICT (guild, user_id)
		DO UPDATE SET level = EXCLUDED.level, xp = EXCLUDED.xp
	`
	_, err := r.db.ExecContext(ctx, q, lvl.GuildID, lvl.UserID, lvl.Level, lvl.XP)
	return err
}

func (r *Repo) SetLevel(ctx context.Context, guildID, userID int64, level int) error {
	const q = `
		INSERT INTO levels (guild, user_id, level, xp)
		VALUES ($1, $2, $3, 0)
		ON CONFLICT (guild, user_id)
		DO UPDATE SET level = EXCLUDED.level, xp = 0
	`
	_, err := r.db.ExecContext(ctx, q, guildID, userID, level)
	return err
}

func (r *Repo) TopLevels(ctx context.Context, guildID int64, limit int) ([]*Level, error) {
	const q = `
		SELECT user_id, level, xp
		FROM levels
		WHERE guild = $1
		ORDER BY level DESC, xp DESC, user_id ASC
		LIMIT $2
	`

	rows, err := r.db.QueryContext(ctx, q, guildID, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]*Level, 0, limit)
	for rows.Next() {
		lvl := &Level{GuildID: guildID}
		if err := rows.Scan(&lvl.UserID, &lvl.Level, &lvl.XP); err != nil {
			return nil, err
		}
		out = append(out, lvl)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return out, nil
}
