Goal.java

package com.distasilucas.cryptobalancetracker.entity;

import com.distasilucas.cryptobalancetracker.model.response.goal.GoalResponse;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

@Entity
@Table(name = "Goals")
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Goal implements Serializable {

    @Id
    private String id;

    @Column(name = "goal_quantity")
    private BigDecimal goalQuantity;

    @OneToOne
    @JoinColumn(name = "crypto_id", nullable = false, unique = true)
    private Crypto crypto;

    public Goal withNewGoalQuantity(BigDecimal newGoalQuantity) {
        return new Goal(this.id, newGoalQuantity, this.crypto);
    }

    public GoalResponse toGoalResponse(List<UserCrypto> userCryptos) {
        var actualQuantity = userCryptos
            .stream()
            .map(UserCrypto::getQuantity)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        var progress = getProgress(actualQuantity);
        var remainingQuantity = getRemainingQuantity(goalQuantity, actualQuantity);
        var moneyNeeded = getMoneyNeeded(remainingQuantity);

        return new GoalResponse(id, crypto.getCryptoInfo().getName(), actualQuantity.toPlainString(), progress,
            remainingQuantity.toPlainString(), goalQuantity.toPlainString(), moneyNeeded.toPlainString());
    }

    private Float getProgress(BigDecimal actualQuantity) {
        return goalQuantity.compareTo(actualQuantity) <= 0 ? 100F :
            actualQuantity.multiply(new BigDecimal("100"))
                .divide(goalQuantity, 2, RoundingMode.HALF_UP)
                .floatValue();
    }

    private BigDecimal getRemainingQuantity(BigDecimal goalQuantity, BigDecimal actualQuantity) {
        return goalQuantity.compareTo(actualQuantity) <= 0 ? BigDecimal.ZERO : goalQuantity.subtract(actualQuantity);
    }

    private BigDecimal getMoneyNeeded(BigDecimal remainingQuantity) {
        return crypto.getLastKnownPrices().getLastKnownPrice().multiply(remainingQuantity).setScale(2, RoundingMode.HALF_UP);
    }

    @Override
    public String toString() {
        return String.format("Goal[id=%s, cryptoName=%s, goalQuantity=%s]", id, crypto.getCryptoInfo().getName(), goalQuantity);
    }
}