package economy

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
)

// ارزها.
const (
	CurrencyCoin   = "coin"
	CurrencyTicket = "ticket"
)

var (
	ErrInsufficient = errors.New("insufficient balance")
	ErrBadCurrency  = errors.New("invalid currency")
)

// column نام ستون موجودی را برای یک ارز برمی‌گرداند.
func column(currency string) (string, error) {
	switch currency {
	case CurrencyCoin:
		return "coins", nil
	case CurrencyTicket:
		return "tickets", nil
	}
	return "", ErrBadCurrency
}

// adjustTx موجودی را داخل یک تراکنش تغییر داده و در دفتر ثبت می‌کند.
// برای delta منفی، اگر موجودی کافی نباشد ErrInsufficient برمی‌گرداند (بدون تغییر).
func adjustTx(ctx context.Context, tx *sql.Tx, userID int64, currency string, delta int64, reason, ref string) error {
	col, err := column(currency)
	if err != nil {
		return err
	}
	var res sql.Result
	if delta < 0 {
		// فقط در صورت کفایت موجودی کم کن (اتمیک)
		res, err = tx.ExecContext(ctx,
			fmt.Sprintf("UPDATE users SET %s = %s + ? WHERE id = ? AND %s >= ?", col, col, col),
			delta, userID, -delta)
	} else {
		res, err = tx.ExecContext(ctx,
			fmt.Sprintf("UPDATE users SET %s = %s + ? WHERE id = ?", col, col),
			delta, userID)
	}
	if err != nil {
		return err
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return ErrInsufficient
	}
	_, err = tx.ExecContext(ctx,
		"INSERT INTO wallet_tx (user_id, amount, reason, ref, currency) VALUES (?, ?, ?, ?, ?)",
		userID, delta, reason, ref, currency)
	return err
}

// Adjust موجودی را در یک تراکنش مستقل تغییر می‌دهد.
func (s *Service) Adjust(ctx context.Context, userID int64, currency string, delta int64, reason, ref string) error {
	tx, err := s.db.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer tx.Rollback()
	if err := adjustTx(ctx, tx, userID, currency, delta, reason, ref); err != nil {
		return err
	}
	return tx.Commit()
}
