MaterialStore.java

package com.university.bookstore.api;

import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;

import com.university.bookstore.model.Material;
import com.university.bookstore.model.Media;

/**
 * Interface defining operations for a polymorphic material store that demonstrates interface segregation and
 * dependency inversion principles. Compare to {@link BookstoreAPI}, this interface is extensible to other types of materials.
 * 
 * <p>The Interface Segregation Principle (ISP) states that clients should not be forced to depend on interfaces they
 * do not use. In practice, this means designing smaller, more focused interfaces rather than large, monolithic ones.
 * For example, instead of creating a single Machine interface with methods like print(), scan(), and fax(), which
 * every implementation must support, ISP encourages splitting these into multiple role-specific interfaces (Printer,
 * Scanner, FaxMachine). This ensures that implementing classes only provide the functionality relevant to them,
 * leading to higher cohesion, reduced coupling, and easier maintenance. ISP directly combats the rigidity and
 * fragility that arise when changes in unused methods propagate across unrelated parts of the system.</p>
 *
 * <p>The Dependency Inversion Principle (DIP) requires that high-level modules depend on abstractions, not concrete
 * implementations. Instead of having business logic directly tied to low-level details (like a specific database
 * driver or logging mechanism), DIP advocates for using interfaces or abstract classes as boundaries. For instance,
 * a PaymentService should rely on a PaymentProcessor interface rather than a hardcoded StripeProcessor.  This allows
 * developers to substitute implementations without altering higher-level policies, supporting testability,
 * scalability, and adaptability to new technologies. By inverting the dependency direction, DIP ensures that both
 * high- and low-level modules evolve independently, fostering loosely coupled, extensible architectures.</p>
 *
 * <p>This interface extends the concept of a bookstore to handle
 * various types of materials using polymorphism.</p>
 *
 * @author Navid Mohaghegh
 * @version 2.0
 * @since 2024-09-15
 */
public interface MaterialStore {

    /**
     * Adds a material to the store inventory.
     *
     * @param material the material to add
     * @return true if added successfully, false if duplicate ID
     */
    boolean addMaterial(Material material);

    /**
     * Removes a material by its ID.
     *
     * @param id the material ID
     * @return the removed material, or Optional.empty() if not found
     */
    Optional<Material> removeMaterial(String id);

    /**
     * Finds a material by its ID.
     *
     * @param id the material ID
     * @return the material, or Optional.empty() if not found
     */
    Optional<Material> findById(String id);

    /**
     * Searches materials by title (case-insensitive partial match).
     *
     * @param title the title to search for
     * @return list of matching materials
     */
    List<Material> searchByTitle(String title);

    /**
     * Searches materials by creator (author/director/publisher).
     *
     * @param creator the creator name
     * @return list of matching materials
     */
    List<Material> searchByCreator(String creator);

    /**
     * Gets all materials of a specific type.
     * Demonstrates polymorphic filtering.
     *
     * @param type the material type
     * @return list of materials of the specified type
     */
    List<Material> getMaterialsByType(Material.MaterialType type);

    /**
     * Gets all materials that implement the Media interface.
     * Demonstrates interface-based polymorphism.
     *
     * @return list of media materials
     */
    List<Media> getMediaMaterials();

    /**
     * Filters materials by a custom predicate.
     * Demonstrates functional programming with polymorphism.
     *
     * @param predicate the filter condition
     * @return filtered list of materials
     */
    List<Material> filterMaterials(Predicate<Material> predicate);

    /**
     * Finds materials published in the last N years.
     *
     * @param years the number of years to look back
     * @return list of recent materials
     */
    List<Material> findRecentMaterials(int years);

    /**
     * Finds materials by multiple creators (OR condition).
     *
     * @param creators the creator names to search for
     * @return list of materials by any of the specified creators
     */
    List<Material> findByCreators(String... creators);

    /**
     * Finds materials with custom filtering using predicate.
     *
     * @param condition the filter condition
     * @return filtered list of materials
     */
    List<Material> findWithPredicate(Predicate<Material> condition);

    /**
     * Gets materials sorted by custom comparator.
     *
     * @param comparator the sorting comparator
     * @return sorted list of materials
     */
    List<Material> getSorted(java.util.Comparator<Material> comparator);

    /**
     * Gets materials within a price range.
     *
     * @param minPrice minimum price (inclusive)
     * @param maxPrice maximum price (inclusive)
     * @return list of materials in price range
     */
    List<Material> getMaterialsByPriceRange(double minPrice, double maxPrice);

    /**
     * Gets materials published/released in a specific year.
     *
     * @param year the year
     * @return list of materials from that year
     */
    List<Material> getMaterialsByYear(int year);

    /**
     * Gets all materials sorted by title.
     *
     * @return sorted list of all materials
     */
    List<Material> getAllMaterialsSorted();

    /**
     * Gets all materials (unsorted).
     *
     * @return list of all materials
     */
    List<Material> getAllMaterials();

    /**
     * Calculates total inventory value.
     *
     * @return sum of all material prices
     */
    double getTotalInventoryValue();

    /**
     * Calculates total discounted inventory value.
     * Uses polymorphic discount calculation.
     *
     * @return sum of all discounted prices
     */
    double getTotalDiscountedValue();

    /**
     * Gets inventory statistics.
     *
     * @return statistics object with counts and averages
     */
    InventoryStats getInventoryStats();

    /**
     * Clears all materials from the store.
     */
    void clearInventory();

    /**
     * Gets the number of materials in inventory.
     *
     * @return material count
     */
    int size();

    /**
     * Checks if the inventory is empty.
     *
     * @return true if no materials in inventory
     */
    boolean isEmpty();

    /**
     * Statistics class for inventory analysis.
     */
    class InventoryStats {
        private final int totalCount;
        private final double averagePrice;
        private final double medianPrice;
        private final int uniqueTypes;
        private final int mediaCount;
        private final int printCount;

        public InventoryStats(int totalCount, double averagePrice, double medianPrice,
                              int uniqueTypes, int mediaCount, int printCount) {
            this.totalCount = totalCount;
            this.averagePrice = averagePrice;
            this.medianPrice = medianPrice;
            this.uniqueTypes = uniqueTypes;
            this.mediaCount = mediaCount;
            this.printCount = printCount;
        }

        public int getTotalCount() {
            return totalCount;
        }

        public double getAveragePrice() {
            return averagePrice;
        }

        public double getMedianPrice() {
            return medianPrice;
        }

        public int getUniqueTypes() {
            return uniqueTypes;
        }

        public int getMediaCount() {
            return mediaCount;
        }

        public int getPrintCount() {
            return printCount;
        }

        @Override
        public String toString() {
            return String.format("InventoryStats[Total=%d, AvgPrice=$%.2f, MedianPrice=$%.2f, Types=%d, Media=%d, Print=%d]",
                    totalCount, averagePrice, medianPrice, uniqueTypes, mediaCount, printCount);
        }
    }
}