package lib

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/bwmarrin/discordgo"
)

type HimbucksEntry struct {
	Username     string
	Balance      int
	MessageCount int
}

func ProcessHimbucks(s *discordgo.Session, m *discordgo.MessageCreate, ctx *ProcessContext) error {
	tx, err := DBClient.Begin()
	if err != nil {
		return fmt.Errorf("failed to start transaction: %w", err)
	}
	defer tx.Rollback()

	// Get current state
	var messageCount int
	var lastRewardAt sql.NullTime

	err = tx.QueryRow(`
        SELECT message_count, last_reward_at
        FROM guild_profiles
        WHERE user_id = ? AND guild_id = ?`,
		ctx.UserID, ctx.GuildID).Scan(&messageCount, &lastRewardAt)
	if err != nil {
		return fmt.Errorf("failed to get message count: %w", err)
	}

	messageCount++
	shouldReward := messageCount >= AppConfig.MessageCountThreshold &&
		(!lastRewardAt.Valid || time.Since(lastRewardAt.Time) >= AppConfig.CooldownPeriod)

	if shouldReward {
		_, err = tx.Exec(`
            UPDATE guild_profiles
            SET currency_balance = currency_balance + ?,
                message_count = 0,
                last_reward_at = CURRENT_TIMESTAMP
            WHERE user_id = ? AND guild_id = ?`,
			AppConfig.HimbucksPerReward, ctx.UserID, ctx.GuildID)
	} else {
		_, err = tx.Exec(`
            UPDATE guild_profiles
            SET message_count = ?
            WHERE user_id = ? AND guild_id = ?`,
			messageCount, ctx.UserID, ctx.GuildID)
	}

	if err != nil {
		return fmt.Errorf("failed to update profile: %w", err)
	}

	return tx.Commit()
}

func GetBalance(discordID, guildID string) (int, error) {
	var balance int
	err := stmtGetBalance.QueryRow(discordID, guildID).Scan(&balance)
	if err == sql.ErrNoRows {
		return 0, nil
	}
	if err != nil {
		return 0, fmt.Errorf("failed to get balance: %w", err)
	}
	return balance, nil
}

func SendBalance(fromDiscordID, toDiscordID, guildID string, amount int) error {
	// Start database transaction
	tx, err := DBClient.Begin()
	if err != nil {
		return fmt.Errorf("failed to start transaction: %w", err)
	}
	defer tx.Rollback()

	// Get sender's user ID
	var fromUserID string
	err = tx.QueryRow(`
        SELECT id
        FROM users
        WHERE discord_id = ?`, fromDiscordID).Scan(&fromUserID)
	if err != nil {
		return fmt.Errorf("sender not found: %w", err)
	}

	// Get recipient's user ID
	var toUserID string
	err = tx.QueryRow(`
        SELECT id
        FROM users
        WHERE discord_id = ?`, toDiscordID).Scan(&toUserID)
	if err != nil {
		return fmt.Errorf("recipient not found: %w", err)
	}

	// Check if sender has sufficient balance
	var senderBalance int
	err = tx.QueryRow(`
        SELECT currency_balance
        FROM guild_profiles
        WHERE user_id = ? AND guild_id = ?`,
		fromUserID, guildID).Scan(&senderBalance)
	if err != nil {
		return fmt.Errorf("failed to get sender balance: %w", err)
	}

	if senderBalance < amount {
		return fmt.Errorf("insufficient balance: have %d, trying to send %d", senderBalance, amount)
	}

	// Deduct from sender
	_, err = tx.Exec(`
        UPDATE guild_profiles
        SET currency_balance = currency_balance - ?
        WHERE user_id = ? AND guild_id = ?`,
		amount, fromUserID, guildID)
	if err != nil {
		return fmt.Errorf("failed to deduct from sender: %w", err)
	}

	// Add to recipient
	result, err := tx.Exec(`
        UPDATE guild_profiles
        SET currency_balance = currency_balance + ?
        WHERE user_id = ? AND guild_id = ?`,
		amount, toUserID, guildID)
	if err != nil {
		return fmt.Errorf("failed to add to recipient: %w", err)
	}

	// Check if recipient exists in guild_profiles
	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("failed to check rows affected: %w", err)
	}

	// If recipient doesn't have a profile in this guild, create one
	if rowsAffected == 0 {
		_, err = tx.Exec(`
            INSERT INTO guild_profiles (user_id, guild_id, currency_balance, message_count)
            VALUES (?, ?, ?, 0)`,
			toUserID, guildID, amount)
		if err != nil {
			return fmt.Errorf("failed to create recipient profile: %w", err)
		}
	}

	// Commit transaction
	if err = tx.Commit(); err != nil {
		return fmt.Errorf("failed to commit transaction: %w", err)
	}

	return nil
}

func GetLeaderboard(guildID string, limit int) ([]HimbucksEntry, error) {
	rows, err := stmtGetLeaderboard.Query(guildID, limit)
	if err != nil {
		return nil, fmt.Errorf("failed to get leaderboard: %w", err)
	}
	defer rows.Close()

	var entries []HimbucksEntry
	for rows.Next() {
		var entry HimbucksEntry
		err := rows.Scan(&entry.Username, &entry.Balance, &entry.MessageCount)
		if err != nil {
			return nil, fmt.Errorf("failed to scan leaderboard entry: %w", err)
		}
		entries = append(entries, entry)
	}
	return entries, nil
}
