InventoryObserver.java

package com.university.bookstore.observer;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.university.bookstore.model.Material;

/**
 * Observer that tracks inventory changes for materials.
 * Maintains counts and statistics for materials in the system.
 * 
 * @author Navid Mohaghegh
 * @version 3.0
 * @since 2024-09-15
 */
public class InventoryObserver implements MaterialObserver {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(InventoryObserver.class);
    
    private final Map<String, Integer> inventoryCounts;
    private final Map<String, Double> totalValue;
    private int totalEvents;
    
    /**
     * Creates a new inventory observer.
     */
    public InventoryObserver() {
        this.inventoryCounts = new HashMap<>();
        this.totalValue = new HashMap<>();
        this.totalEvents = 0;
    }
    
    @Override
    public void onEvent(MaterialEvent event) {
        totalEvents++;
        
        switch (event.getEventType()) {
            case "MATERIAL_ADDED":
                handleMaterialAdded(event);
                break;
            case "PRICE_CHANGED":
                handlePriceChanged(event);
                break;
            default:
                // Handle other event types if needed
                break;
        }
    }
    
    /**
     * Handles material added events.
     */
    private void handleMaterialAdded(MaterialEvent event) {
        Material material = event.getMaterial();
        String materialId = material.getId();
        
        // Update inventory count
        inventoryCounts.merge(materialId, 1, Integer::sum);
        
        // Update total value
        totalValue.merge(materialId, material.getPrice(), Double::sum);
        
        LOGGER.info("Inventory updated: {} count: {}", 
            materialId, inventoryCounts.get(materialId));
    }
    
    /**
     * Handles price changed events.
     */
    private void handlePriceChanged(MaterialEvent event) {
        if (event instanceof PriceChangedEvent) {
            PriceChangedEvent priceEvent = (PriceChangedEvent) event;
            Material material = event.getMaterial();
            String materialId = material.getId();
            
            // Update total value with the price change
            double priceChange = priceEvent.getPriceChange();
            totalValue.merge(materialId, priceChange, Double::sum);
            
            LOGGER.info("Price changed for {}: ${} -> ${}", 
                material.getTitle(), priceEvent.getOldPrice(), priceEvent.getNewPrice());
        }
    }
    
    /**
     * Gets the inventory count for a specific material.
     * 
     * @param materialId the material ID
     * @return the inventory count
     */
    public int getInventoryCount(String materialId) {
        return inventoryCounts.getOrDefault(materialId, 0);
    }
    
    /**
     * Gets the total value for a specific material.
     * 
     * @param materialId the material ID
     * @return the total value
     */
    public double getTotalValue(String materialId) {
        return totalValue.getOrDefault(materialId, 0.0);
    }
    
    /**
     * Gets the total number of materials in inventory.
     * 
     * @return the total count
     */
    public int getTotalInventoryCount() {
        return inventoryCounts.values().stream()
            .mapToInt(Integer::intValue)
            .sum();
    }
    
    /**
     * Gets the total value of all materials in inventory.
     * 
     * @return the total value
     */
    public double getTotalInventoryValue() {
        return totalValue.values().stream()
            .mapToDouble(Double::doubleValue)
            .sum();
    }
    
    /**
     * Gets the number of unique materials in inventory.
     * 
     * @return the unique material count
     */
    public int getUniqueMaterialCount() {
        return inventoryCounts.size();
    }
    
    /**
     * Gets the total number of events processed.
     * 
     * @return the event count
     */
    public int getTotalEvents() {
        return totalEvents;
    }
    
    /**
     * Gets all inventory counts.
     * 
     * @return map of material ID to count
     */
    public Map<String, Integer> getAllInventoryCounts() {
        return new HashMap<>(inventoryCounts);
    }
    
    /**
     * Gets all total values.
     * 
     * @return map of material ID to total value
     */
    public Map<String, Double> getAllTotalValues() {
        return new HashMap<>(totalValue);
    }
    
    /**
     * Clears all inventory data.
     */
    public void clear() {
        inventoryCounts.clear();
        totalValue.clear();
        totalEvents = 0;
    }
    
    @Override
    public String getObserverName() {
        return "InventoryObserver";
    }
    
    @Override
    public String toString() {
        return String.format("InventoryObserver[Materials=%d, TotalCount=%d, TotalValue=$%.2f, Events=%d]",
            getUniqueMaterialCount(), getTotalInventoryCount(), getTotalInventoryValue(), getTotalEvents());
    }
}