package settingsrepo

import (
	"context"
	"database/sql"
)

type Repo struct {
	db *sql.DB
}

type LevelReward struct {
	GuildID int64
	Level   int
	RoleID  int64
}

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

func (r *Repo) SetLevelSystemEnabled(ctx context.Context, guildID int64, enabled bool) error {
	const q = `
		INSERT INTO levelsettings (guild_id, levelsys)
		VALUES ($1, $2)
		ON CONFLICT (guild_id)
		DO UPDATE SET levelsys = EXCLUDED.levelsys
	`
	_, err := r.db.ExecContext(ctx, q, guildID, enabled)
	return err
}

func (r *Repo) IsLevelSystemEnabled(ctx context.Context, guildID int64) (bool, error) {
	const q = `
		SELECT levelsys
		FROM levelsettings
		WHERE guild_id = $1
	`
	row := r.db.QueryRowContext(ctx, q, guildID)
	var enabled bool
	switch err := row.Scan(&enabled); err {
	case nil:
		return enabled, nil
	case sql.ErrNoRows:
		return false, nil
	default:
		return false, err
	}
}

func (r *Repo) GetLevelupChannelID(ctx context.Context, guildID int64) (int64, bool, error) {
	const q = `
		SELECT levelup_channel_id
		FROM levelup
		WHERE guild_id = $1
	`
	row := r.db.QueryRowContext(ctx, q, guildID)
	var channelID sql.NullInt64
	switch err := row.Scan(&channelID); err {
	case nil:
		if !channelID.Valid {
			return 0, false, nil
		}
		return channelID.Int64, true, nil
	case sql.ErrNoRows:
		return 0, false, nil
	default:
		return 0, false, err
	}
}

func (r *Repo) GetRoleRewardForLevel(ctx context.Context, guildID int64, level int) (roleID int64, ok bool, err error) {
	const q = `
		SELECT role
		FROM levelrewards
		WHERE guild_id = $1 AND levelreq = $2
	`
	row := r.db.QueryRowContext(ctx, q, guildID, level)
	var role sql.NullInt64
	switch err := row.Scan(&role); err {
	case nil:
		if !role.Valid {
			return 0, false, nil
		}
		return role.Int64, true, nil
	case sql.ErrNoRows:
		return 0, false, nil
	default:
		return 0, false, err
	}
}

func (r *Repo) SetRoleRewardForLevel(ctx context.Context, guildID int64, level int, roleID int64) error {
	const q = `
		INSERT INTO levelrewards (guild_id, levelreq, role)
		VALUES ($1, $2, $3)
		ON CONFLICT (guild_id, levelreq)
		DO UPDATE SET role = EXCLUDED.role
	`
	_, err := r.db.ExecContext(ctx, q, guildID, level, roleID)
	return err
}

func (r *Repo) ListRoleRewards(ctx context.Context, guildID int64, limit int) ([]*LevelReward, error) {
	const q = `
		SELECT levelreq, role
		FROM levelrewards
		WHERE guild_id = $1
		ORDER BY levelreq ASC
		LIMIT $2
	`
	rows, err := r.db.QueryContext(ctx, q, guildID, limit)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

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

func (r *Repo) SetLevelUpChannel(ctx context.Context, guildID int64, channelID int64) error {
	const q = `
		INSERT INTO levelup (guild_id, levelup_channel_id)
		VALUES ($1, $2)
		ON CONFLICT (guild_id)
		DO UPDATE SET levelup_channel_id = EXCLUDED.levelup_channel_id
	`
	_, err := r.db.ExecContext(ctx, q, guildID, channelID)
	return err
}

func (r *Repo) SetLevelUpMessage(ctx context.Context, guildID int64, message string) error {
	const q = `
		UPDATE levelsettings
		SET message = $1
		WHERE guild_id = $2
	`
	_, err := r.db.ExecContext(ctx, q, message, guildID)
	return err
}

func (r *Repo) GetLevelUpMessage(ctx context.Context, guildID int64) (string, error) {
	const q = `
		SELECT message
		FROM levelsettings
		WHERE guild_id = $1
	`
	row := r.db.QueryRowContext(ctx, q, guildID)
	var message sql.NullString
	switch err := row.Scan(&message); err {
	case nil:
		if !message.Valid {
			return "GG {user}, you reached level {level}!", nil
		}
		return message.String, nil
	case sql.ErrNoRows:
		return "GG {user}, you reached level {level}!", nil
	default:
		return "", err
	}
}
