package service

import (
	"context"
	"errors"
	"fmt"
	"sno/internal/models"
	"sno/internal/repository"

	"github.com/jackc/pgx/v5/pgxpool"
)

// RewardService defines the interface for reward-related business logic.
type RewardService interface {
	CreateReward(ctx context.Context, reward *models.Reward) (*models.Reward, error)
	ListActiveRewards(ctx context.Context) ([]models.Reward, error)
	PurchaseReward(ctx context.Context, userID int64, rewardID int) (*models.Purchase, error)
	UpdateReward(ctx context.Context, id int, reward *models.Reward) (*models.Reward, error)
	DeleteReward(ctx context.Context, id int) error
}

// rewardService implements the RewardService interface.
type rewardService struct {
	db           *pgxpool.Pool
	rewardRepo   repository.RewardRepository
	userRepo     repository.UserRepository
	purchaseRepo repository.PurchaseRepository
}

// NewRewardService creates a new instance of a reward service.
func NewRewardService(db *pgxpool.Pool, rewardRepo repository.RewardRepository, userRepo repository.UserRepository, purchaseRepo repository.PurchaseRepository) RewardService {
	return &rewardService{
		db:           db,
		rewardRepo:   rewardRepo,
		userRepo:     userRepo,
		purchaseRepo: purchaseRepo,
	}
}

// CreateReward handles the business logic for creating a new reward.
func (s *rewardService) CreateReward(ctx context.Context, reward *models.Reward) (*models.Reward, error) {
	if reward.Title == "" {
		return nil, errors.New("reward title cannot be empty")
	}
	if reward.PriceStars <= 0 {
		return nil, errors.New("reward price must be positive")
	}
	return s.rewardRepo.Create(ctx, reward)
}

// ListActiveRewards retrieves a list of all active rewards.
func (s *rewardService) ListActiveRewards(ctx context.Context) ([]models.Reward, error) {
	return s.rewardRepo.GetAllActive(ctx)
}

// PurchaseReward handles the logic for a user purchasing a reward.
func (s *rewardService) PurchaseReward(ctx context.Context, userID int64, rewardID int) (*models.Purchase, error) {
	// 1. Get reward and user details
	reward, err := s.rewardRepo.GetByID(ctx, rewardID)
	if err != nil {
		return nil, fmt.Errorf("reward not found: %w", err)
	}

	user, err := s.userRepo.GetByID(ctx, userID)
	if err != nil {
		return nil, fmt.Errorf("user not found: %w", err)
	}

	// 2. Validate purchase
	if !reward.IsActive {
		return nil, errors.New("reward is not active")
	}
	if reward.Stock == 0 { // -1 is infinite
		return nil, errors.New("reward is out of stock")
	}
	if user.StarsBalance < reward.PriceStars {
		return nil, errors.New("not enough stars")
	}

	// 3. Start transaction
	tx, err := s.db.Begin(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer tx.Rollback(ctx)
	txCtx := context.WithValue(ctx, "tx", tx)

	// 4. Perform operations
	// a. Create purchase record
	purchase := &models.Purchase{
		UserID:     userID,
		RewardID:   rewardID,
		StarsSpent: reward.PriceStars,
		Status:     models.Pending,
	}
	createdPurchase, err := s.purchaseRepo.Create(txCtx, purchase)
	if err != nil {
		return nil, fmt.Errorf("failed to create purchase record: %w", err)
	}

	// b. Decrement user stars
	if err := s.userRepo.UpdateStarsBalance(txCtx, userID, -reward.PriceStars); err != nil {
		return nil, fmt.Errorf("failed to update user balance: %w", err)
	}

	// c. Decrement stock (if not infinite)
	if reward.Stock != -1 {
		if err := s.rewardRepo.UpdateStock(txCtx, rewardID, 1); err != nil {
			return nil, fmt.Errorf("failed to update reward stock: %w", err)
		}
	}

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

	return createdPurchase, nil
}

// UpdateReward updates an existing reward
func (s *rewardService) UpdateReward(ctx context.Context, id int, reward *models.Reward) (*models.Reward, error) {
	// Check if reward exists
	existingReward, err := s.rewardRepo.GetByID(ctx, id)
	if err != nil {
		return nil, fmt.Errorf("failed to get existing reward: %w", err)
	}
	if existingReward == nil {
		return nil, errors.New("reward not found")
	}

	// Update reward
	updatedReward, err := s.rewardRepo.Update(ctx, id, reward)
	if err != nil {
		return nil, fmt.Errorf("failed to update reward: %w", err)
	}

	return updatedReward, nil
}

// DeleteReward deletes a reward
func (s *rewardService) DeleteReward(ctx context.Context, id int) error {
	// Check if reward exists
	existingReward, err := s.rewardRepo.GetByID(ctx, id)
	if err != nil {
		return fmt.Errorf("failed to get existing reward: %w", err)
	}
	if existingReward == nil {
		return errors.New("reward not found")
	}

	// Delete reward
	err = s.rewardRepo.Delete(ctx, id)
	if err != nil {
		return fmt.Errorf("failed to delete reward: %w", err)
	}

	return nil
}
