PriceChangedEvent.java

package com.university.bookstore.observer;

import java.util.Objects;

import com.university.bookstore.model.Material;

/**
 * Event that occurs when a material's price changes.
 * 
 * @author Navid Mohaghegh
 * @version 3.0
 * @since 2024-09-15
 */
public class PriceChangedEvent implements MaterialEvent {
    
    private final Material material;
    private final double oldPrice;
    private final double newPrice;
    private final long timestamp;
    
    /**
     * Creates a new price changed event.
     * 
     * @param material the material whose price changed
     * @param oldPrice the previous price
     * @param newPrice the new price
     * @throws IllegalArgumentException if material is null or prices are negative
     */
    public PriceChangedEvent(Material material, double oldPrice, double newPrice) {
        this.material = Objects.requireNonNull(material, "Material cannot be null");
        if (oldPrice < 0) {
            throw new IllegalArgumentException("Old price cannot be negative: " + oldPrice);
        }
        if (newPrice < 0) {
            throw new IllegalArgumentException("New price cannot be negative: " + newPrice);
        }
        
        this.oldPrice = oldPrice;
        this.newPrice = newPrice;
        this.timestamp = System.currentTimeMillis();
    }
    
    @Override
    public Material getMaterial() {
        return material;
    }
    
    @Override
    public long getTimestamp() {
        return timestamp;
    }
    
    @Override
    public String getEventType() {
        return "PRICE_CHANGED";
    }
    
    @Override
    public String getDescription() {
        return String.format("Price changed for %s: $%.2f -> $%.2f (Change: $%.2f)",
            material.getTitle(), oldPrice, newPrice, newPrice - oldPrice);
    }
    
    /**
     * Gets the old price.
     * 
     * @return the previous price
     */
    public double getOldPrice() {
        return oldPrice;
    }
    
    /**
     * Gets the new price.
     * 
     * @return the new price
     */
    public double getNewPrice() {
        return newPrice;
    }
    
    /**
     * Gets the price change amount.
     * 
     * @return the price change (new - old)
     */
    public double getPriceChange() {
        return newPrice - oldPrice;
    }
    
    /**
     * Gets the price change percentage.
     * 
     * @return the price change percentage
     */
    public double getPriceChangePercentage() {
        if (oldPrice == 0) {
            return newPrice > 0 ? 100.0 : 0.0;
        }
        return ((newPrice - oldPrice) / oldPrice) * 100.0;
    }
    
    /**
     * Checks if the price increased.
     * 
     * @return true if the price increased
     */
    public boolean isPriceIncrease() {
        return newPrice > oldPrice;
    }
    
    /**
     * Checks if the price decreased.
     * 
     * @return true if the price decreased
     */
    public boolean isPriceDecrease() {
        return newPrice < oldPrice;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        
        PriceChangedEvent that = (PriceChangedEvent) obj;
        return Double.compare(that.oldPrice, oldPrice) == 0 &&
               Double.compare(that.newPrice, newPrice) == 0 &&
               timestamp == that.timestamp &&
               Objects.equals(material, that.material);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(material, oldPrice, newPrice, timestamp);
    }
    
    @Override
    public String toString() {
        return String.format("PriceChangedEvent[%s: $%.2f -> $%.2f at %d]",
            material.getTitle(), oldPrice, newPrice, timestamp);
    }
}