package ru.soune.nocopy.service.payout;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.entity.referral.Referral;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.InsufficientFundsException;
import ru.soune.nocopy.repository.ReferralJpaRepository;

import java.math.BigDecimal;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class BalanceService {

    private final ReferralJpaRepository referralJpaRepository;

    public boolean hasSufficientFunds(User user, BigDecimal amount) {
        Referral referral = referralJpaRepository.findByUserId(user.getId());
        BigDecimal available = BigDecimal.valueOf(referral.getAvailableIncome());

        return available.compareTo(amount) >= 0;
    }

    public BigDecimal getBalance(User user) {
        Referral referral = referralJpaRepository.findByUserId(user.getId());
        return BigDecimal.valueOf(referral.getAvailableIncome());
    }

    public BigDecimal getFrozenBalance(User user) {
        Referral referral = referralJpaRepository.findByUserId(user.getId());
        return BigDecimal.valueOf(referral.getHoldBalance());
    }

    @Transactional
    public void freezeAmount(User user, BigDecimal amount) {
        Referral referral = referralJpaRepository.findByUserId(user.getId());
        BigDecimal available = BigDecimal.valueOf(referral.getAvailableIncome());

        if (available.compareTo(amount) < 0) {
            throw new InsufficientFundsException(
                    String.format("Not have amount. Amount have: %.2f", available)
            );
        }

        BigDecimal newAvailable = available.subtract(amount);
        BigDecimal newHold = BigDecimal.valueOf(referral.getHoldBalance()).add(amount);

        referral.setAvailableIncome(newAvailable.intValue());
        referral.setHoldBalance(newHold.intValue());

        referralJpaRepository.save(referral);

        log.info("Amount freeze. Have: {}, Frozen: {}", newAvailable, newHold);
    }

    @Transactional
    public void unfreezeAmount(Long userId, BigDecimal amount) {
        log.info("Unfreeze {} for user {}", amount, userId);

        Referral referral = referralJpaRepository.findByUserId(userId);
        BigDecimal hold = BigDecimal.valueOf(referral.getHoldBalance());

        if (hold.compareTo(amount) < 0) {
            throw new IllegalStateException(
                    String.format("Not have freeze amount. Frozen: %.2f", hold)
            );
        }

        BigDecimal newAvailable = BigDecimal.valueOf(referral.getAvailableIncome()).add(amount);
        BigDecimal newHold = hold.subtract(amount);

        referral.setAvailableIncome(newAvailable.intValue());
        referral.setHoldBalance(newHold.intValue());

        referralJpaRepository.save(referral);

        log.info("Amount frozen. Have: {}, Frozen: {}", newAvailable, newHold);
    }

    public void withdrawAmount(Long userId, BigDecimal amount) {
        Referral referral = referralJpaRepository.findByUserId(userId);

        if (referral.getHoldBalance() < amount.intValue()) {
            throw new IllegalStateException("Not have amount");
        }

        referral.setHoldBalance(referral.getHoldBalance() - amount.intValue());
    }
}