package level

import (
	"context"
	"math/rand"
	"time"

	"velox-bot/internal/db/repos/levelrepo"
)

type SettingsProvider interface {
	IsLevelSystemEnabled(ctx context.Context, guildID int64) (bool, error)
	GetLevelupChannelID(ctx context.Context, guildID int64) (int64, bool, error)
	GetRoleRewardForLevel(ctx context.Context, guildID int64, level int) (roleID int64, ok bool, err error)
	SetLevelUpChannel(ctx context.Context, guildID int64, channelID int64) error
	SetLevelUpMessage(ctx context.Context, guildID int64, message string) error
	GetLevelUpMessage(ctx context.Context, guildID int64) (string, error)
}

type Result struct {
	LeveledUp      bool
	NewLevel       int
	RoleRewardID   int64  // 0 if none
	LevelupMessage string // you can fill this later
	LevelupChannel int64  // 0 means "use current channel"
	UpdatedXP      int64
	UpdatedLevel   int
}

type Service struct {
	levels   *levelrepo.Repo
	settings SettingsProvider
	rand     *rand.Rand
}

func New(levels *levelrepo.Repo, settings SettingsProvider) *Service {
	return &Service{
		levels:   levels,
		settings: settings,
		rand:     rand.New(rand.NewSource(time.Now().UnixNano())),
	}
}

// HandleMessage mirrors your Python gain_xp behavior, minus Discord specifics.
func (s *Service) HandleMessage(ctx context.Context, guildID, userID int64) (*Result, error) {
	enabled, err := s.settings.IsLevelSystemEnabled(ctx, guildID)
	if err != nil || !enabled {
		return &Result{LeveledUp: false}, err
	}

	lvl, err := s.levels.GetLevel(ctx, guildID, userID)
	if err != nil {
		return nil, err
	}

	// XP gain rules
	shouldGainXP := false
	if lvl.Level < 5 {
		shouldGainXP = true
	} else {
		// random.randint(1, level//4) == 1
		div := lvl.Level / 4
		if div < 1 {
			div = 1
		}
		if s.rand.Intn(div)+1 == 1 {
			shouldGainXP = true
		}
	}

	if !shouldGainXP {
		return &Result{
			LeveledUp:    false,
			UpdatedXP:    lvl.XP,
			UpdatedLevel: lvl.Level,
		}, nil
	}

	// This assumes your setXp() adds some fixed XP per hit; adapt amount as needed.
	xpPerMessage := s.rand.Intn(3) + 1
	lvl.XP += int64(xpPerMessage)

	res := &Result{
		LeveledUp:    false,
		UpdatedXP:    lvl.XP,
		UpdatedLevel: lvl.Level,
	}

	// Level-up rule: if xp >= 100 -> level++
	if lvl.XP >= 100 {
		lvl.Level++
		lvl.XP = 0 // or keep overflow, depending on Python behavior

		res.LeveledUp = true
		res.NewLevel = lvl.Level
		res.UpdatedXP = lvl.XP
		res.UpdatedLevel = lvl.Level

		roleID, ok, err := s.settings.GetRoleRewardForLevel(ctx, guildID, lvl.Level)
		if err != nil {
			return nil, err
		}
		if ok {
			res.RoleRewardID = roleID
		}

		chID, ok, err := s.settings.GetLevelupChannelID(ctx, guildID)
		if err != nil {
			return nil, err
		}
		if ok {
			res.LevelupChannel = chID
		}
	}

	// Persist
	if err := s.levels.UpsertLevel(ctx, lvl); err != nil {
		return nil, err
	}

	return res, nil
}

func (s *Service) SetLevel(ctx context.Context, guildID, userID int64, level int) error {
	return s.levels.SetLevel(ctx, guildID, userID, level)
}

func (s *Service) GetLevel(ctx context.Context, guildID, userID int64) (*levelrepo.Level, error) {
	return s.levels.GetLevel(ctx, guildID, userID)
}

func (s *Service) TopLevels(ctx context.Context, guildID int64, limit int) ([]*levelrepo.Level, error) {
	return s.levels.TopLevels(ctx, guildID, limit)
}
