# ModernConcurrentMaterialStore.java

    package com.university.bookstore.impl;

    import java.util.*;
    import java.util.concurrent.*;
    import java.util.concurrent.locks.StampedLock;
    import java.util.function.Predicate;
    import java.util.stream.Collectors;

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

    import com.university.bookstore.api.MaterialStore;
    import com.university.bookstore.model.Magazine;
    import com.university.bookstore.model.Material;
    import com.university.bookstore.model.Media;
    import com.university.bookstore.model.PrintedBook;

    /**
     * Modern thread-safe implementation of MaterialStore using best practices.
     * Features:
     * - StampedLock for optimized read performance
     * - ExecutorService for async operations
     * - CompletableFuture for non-blocking operations
     * - Proper resource management with AutoCloseable
     * - Virtual thread support (when available)
     *
     * @author Navid Mohaghegh
     * @version 4.0
     * @since 2024-09-15
     */
    public class ModernConcurrentMaterialStore implements MaterialStore, AutoCloseable {

        private static final Logger LOGGER = LoggerFactory.getLogger(ModernConcurrentMaterialStore.class);

        private final Map<String, Material> materials;
        private final StampedLock stampedLock;
        private final ExecutorService executorService;
        private final ScheduledExecutorService scheduledExecutor;
        private volatile boolean closed = false;

        /**
         * Creates a new modern thread-safe material store.
         */
        public ModernConcurrentMaterialStore() {
            this.materials = new ConcurrentHashMap<>();
            this.stampedLock = new StampedLock();

            // Use ForkJoinPool for better work-stealing behavior
            this.executorService = new ForkJoinPool(
                Runtime.getRuntime().availableProcessors(),
                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                null,
                true // Enable async mode for better throughput
            );

            this.scheduledExecutor = Executors.newScheduledThreadPool(2, r -> {
                Thread t = new Thread(r, "MaterialStore-Scheduler");
                t.setDaemon(true);
                return t;
            });

            // Schedule periodic cleanup tasks
            scheduleMaintenanceTasks();
        }

        /**
         * Creates a material store with initial materials.
         *
         * @param initialMaterials materials to add initially
         */
        public ModernConcurrentMaterialStore(Collection<Material> initialMaterials) {
            this();
            if (initialMaterials != null) {
                // Parallel addition for better performance
                initialMaterials.parallelStream().forEach(this::addMaterial);
            }
        }

        private void scheduleMaintenanceTasks() {
            // Example: periodic cache cleanup or metrics collection
            scheduledExecutor.scheduleAtFixedRate(
                this::performMaintenance,
                1, 1, TimeUnit.HOURS
            );
        }

        private void performMaintenance() {
            if (!closed) {
                // Maintenance tasks like clearing old data, collecting metrics, etc.
                // This is a placeholder for actual maintenance logic
            }
        }

        @Override
        public boolean addMaterial(Material material) {
            Objects.requireNonNull(material, "Material cannot be null");
            ensureNotClosed();

            long stamp = stampedLock.writeLock();
            try {
                return materials.putIfAbsent(material.getId(), material) == null;
            } finally {
                stampedLock.unlockWrite(stamp);
            }
        }

        /**
         * Adds material asynchronously.
         *
         * @param material the material to add
         * @return CompletableFuture with the result
         */
        public CompletableFuture<Boolean> addMaterialAsync(Material material) {
            return CompletableFuture.supplyAsync(
                () -> addMaterial(material),
                executorService
            );
        }

        /**
         * Adds multiple materials in batch asynchronously.
         *
         * @param materials collection of materials to add
         * @return CompletableFuture with results map
         */
        public CompletableFuture<Map<String, Boolean>> addMaterialsBatchAsync(Collection<Material> materials) {
            List<CompletableFuture<Map.Entry<String, Boolean>>> futures = materials.stream()
                .map(material -> CompletableFuture.supplyAsync(
                    () -> Map.entry(material.getId(), addMaterial(material)),
                    executorService
                ))
                .toList();

            return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .thenApply(v -> futures.stream()
                    .map(CompletableFuture::join)
                    .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue
                    )));
        }

        @Override
        public Optional<Material> removeMaterial(String id) {
            if (id == null) {
                return Optional.empty();
            }
            ensureNotClosed();

            long stamp = stampedLock.writeLock();
            try {
                return Optional.ofNullable(materials.remove(id));
            } finally {
                stampedLock.unlockWrite(stamp);
            }
        }

        @Override
        public Optional<Material> findById(String id) {
            if (id == null) {
                return Optional.empty();
            }
            ensureNotClosed();

            // Try optimistic read first for better performance
            long stamp = stampedLock.tryOptimisticRead();
            Material material = materials.get(id);

            if (!stampedLock.validate(stamp)) {
                // Optimistic read failed, acquire read lock
                stamp = stampedLock.readLock();
                try {
                    material = materials.get(id);
                } finally {
                    stampedLock.unlockRead(stamp);
                }
            }

            return Optional.ofNullable(material);
        }

        /**
         * Finds material by ID asynchronously.
         *
         * @param id the material ID
         * @return CompletableFuture with the result
         */
        public CompletableFuture<Optional<Material>> findByIdAsync(String id) {
            return CompletableFuture.supplyAsync(
                () -> findById(id),
                executorService
            );
        }

        /**
         * Finds multiple materials by IDs asynchronously.
         *
         * @param ids list of material IDs
         * @return CompletableFuture with results map
         */
        public CompletableFuture<Map<String, Material>> findByIdsAsync(List<String> ids) {
            List<CompletableFuture<Map.Entry<String, Optional<Material>>>> futures = ids.stream()
                .map(id -> CompletableFuture.supplyAsync(
                    () -> Map.entry(id, findById(id)),
                    executorService
                ))
                .toList();

            return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .thenApply(v -> futures.stream()
                    .map(CompletableFuture::join)
                    .filter(entry -> entry.getValue().isPresent())
                    .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().get()
                    )));
        }

        @Override
        public List<Material> searchByTitle(String title) {
            if (title == null || title.trim().isEmpty()) {
                return new ArrayList<>();
            }
            ensureNotClosed();

            String searchTerm = title.toLowerCase().trim();
            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(m -> m.getTitle().toLowerCase().contains(searchTerm))
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        /**
         * Searches by title asynchronously.
         *
         * @param title the title to search for
         * @return CompletableFuture with the results
         */
        public CompletableFuture<List<Material>> searchByTitleAsync(String title) {
            return CompletableFuture.supplyAsync(
                () -> searchByTitle(title),
                executorService
            );
        }

        @Override
        public List<Material> searchByCreator(String creator) {
            if (creator == null || creator.trim().isEmpty()) {
                return new ArrayList<>();
            }
            ensureNotClosed();

            String searchTerm = creator.toLowerCase().trim();
            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(m -> m.getCreator().toLowerCase().contains(searchTerm))
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> getMaterialsByType(Material.MaterialType type) {
            if (type == null) {
                return new ArrayList<>();
            }
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(m -> m.getType() == type)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Media> getMediaMaterials() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(Media.class::isInstance)
                    .map(Media.class::cast)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> filterMaterials(Predicate<Material> predicate) {
            Objects.requireNonNull(predicate, "Predicate cannot be null");
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(predicate)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> getMaterialsByPriceRange(double minPrice, double maxPrice) {
            if (minPrice < 0 || maxPrice < 0 || minPrice > maxPrice) {
                return new ArrayList<>();
            }
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(m -> m.getPrice() >= minPrice && m.getPrice() <= maxPrice)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> getMaterialsByYear(int year) {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(m -> m.getYear() == year)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> getAllMaterialsSorted() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().stream()
                    .sorted()
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> getAllMaterials() {
            ensureNotClosed();

            // Use optimistic read for better performance
            long stamp = stampedLock.tryOptimisticRead();
            List<Material> result = new ArrayList<>(materials.values());

            if (!stampedLock.validate(stamp)) {
                stamp = stampedLock.readLock();
                try {
                    result = new ArrayList<>(materials.values());
                } finally {
                    stampedLock.unlockRead(stamp);
                }
            }

            return result;
        }

        @Override
        public double getTotalInventoryValue() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .mapToDouble(Material::getPrice)
                    .sum();
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        /**
         * Gets total inventory value asynchronously.
         *
         * @return CompletableFuture with the total value
         */
        public CompletableFuture<Double> getTotalInventoryValueAsync() {
            return CompletableFuture.supplyAsync(
                this::getTotalInventoryValue,
                executorService
            );
        }

        @Override
        public double getTotalDiscountedValue() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .mapToDouble(Material::getDiscountedPrice)
                    .sum();
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public InventoryStats getInventoryStats() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                if (materials.isEmpty()) {
                    return new InventoryStats(0, 0, 0, 0, 0, 0);
                }

                List<Double> prices = materials.values().stream()
                    .map(Material::getPrice)
                    .sorted()
                    .collect(Collectors.toList());

                double averagePrice = prices.stream()
                    .mapToDouble(Double::doubleValue)
                    .average()
                    .orElse(0.0);

                double medianPrice = prices.size() % 2 == 0
                    ? (prices.get(prices.size() / 2 - 1) + prices.get(prices.size() / 2)) / 2
                    : prices.get(prices.size() / 2);

                int uniqueTypes = (int) materials.values().stream()
                    .map(Material::getType)
                    .distinct()
                    .count();

                int mediaCount = (int) materials.values().stream()
                    .filter(Media.class::isInstance)
                    .count();

                int printCount = (int) materials.values().stream()
                    .filter(m -> m instanceof PrintedBook || m instanceof Magazine)
                    .count();

                return new InventoryStats(
                    materials.size(),
                    averagePrice,
                    medianPrice,
                    uniqueTypes,
                    mediaCount,
                    printCount
                );
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        /**
         * Gets inventory statistics asynchronously.
         *
         * @return CompletableFuture with the statistics
         */
        public CompletableFuture<InventoryStats> getInventoryStatsAsync() {
            return CompletableFuture.supplyAsync(
                this::getInventoryStats,
                executorService
            );
        }

        @Override
        public void clearInventory() {
            ensureNotClosed();

            long stamp = stampedLock.writeLock();
            try {
                materials.clear();
            } finally {
                stampedLock.unlockWrite(stamp);
            }
        }

        @Override
        public int size() {
            ensureNotClosed();

            // Use optimistic read for size check
            long stamp = stampedLock.tryOptimisticRead();
            int size = materials.size();

            if (!stampedLock.validate(stamp)) {
                stamp = stampedLock.readLock();
                try {
                    size = materials.size();
                } finally {
                    stampedLock.unlockRead(stamp);
                }
            }

            return size;
        }

        @Override
        public boolean isEmpty() {
            return size() == 0;
        }

        @Override
        public List<Material> findRecentMaterials(int years) {
            if (years < 0) {
                throw new IllegalArgumentException("Years cannot be negative: " + years);
            }
            ensureNotClosed();

            int currentYear = java.time.Year.now().getValue();
            int cutoffYear = currentYear - years;

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(material -> material.getYear() >= cutoffYear)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> findByCreators(String... creators) {
            if (creators == null || creators.length == 0) {
                return new ArrayList<>();
            }
            ensureNotClosed();

            Set<String> creatorSet = Arrays.stream(creators)
                .filter(Objects::nonNull)
                .map(String::trim)
                .filter(s -> !s.isEmpty())
                .collect(Collectors.toSet());

            if (creatorSet.isEmpty()) {
                return new ArrayList<>();
            }

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(material -> creatorSet.contains(material.getCreator()))
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> findWithPredicate(Predicate<Material> condition) {
            Objects.requireNonNull(condition, "Predicate cannot be null");
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(condition)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        @Override
        public List<Material> getSorted(Comparator<Material> comparator) {
            Objects.requireNonNull(comparator, "Comparator cannot be null");
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().stream()
                    .sorted(comparator)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        /**
         * Performs parallel search across multiple criteria.
         *
         * @param title optional title search term
         * @param creator optional creator search term
         * @param type optional material type
         * @return CompletableFuture with combined results
         */
        public CompletableFuture<List<Material>> parallelSearchAsync(
                String title, String creator, Material.MaterialType type) {

            List<CompletableFuture<List<Material>>> searches = new ArrayList<>();

            if (title != null && !title.trim().isEmpty()) {
                searches.add(searchByTitleAsync(title));
            }
            if (creator != null && !creator.trim().isEmpty()) {
                searches.add(CompletableFuture.supplyAsync(
                    () -> searchByCreator(creator), executorService));
            }
            if (type != null) {
                searches.add(CompletableFuture.supplyAsync(
                    () -> getMaterialsByType(type), executorService));
            }

            if (searches.isEmpty()) {
                return CompletableFuture.completedFuture(new ArrayList<>());
            }

            return CompletableFuture.allOf(searches.toArray(new CompletableFuture[0]))
                .thenApply(v -> searches.stream()
                    .map(CompletableFuture::join)
                    .flatMap(List::stream)
                    .distinct()
                    .collect(Collectors.toList()));
        }

        /**
         * Groups materials by type for reporting.
         *
         * @return map of type to materials
         */
        public Map<Material.MaterialType, List<Material>> groupByType() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().stream()
                    .collect(Collectors.groupingBy(Material::getType));
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        /**
         * Gets materials with active discounts.
         *
         * @return list of discounted materials
         */
        public List<Material> getDiscountedMaterials() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .filter(m -> m.getDiscountRate() > 0)
                    .collect(Collectors.toList());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        /**
         * Calculates total savings from discounts.
         *
         * @return total discount amount
         */
        public double getTotalDiscountAmount() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return materials.values().parallelStream()
                    .mapToDouble(m -> m.getPrice() * m.getDiscountRate())
                    .sum();
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }

        private void ensureNotClosed() {
            if (closed) {
                throw new IllegalStateException("MaterialStore has been closed");
            }
        }

        @Override
        public void close() {
            if (!closed) {
                closed = true;

                // Shutdown executors gracefully
                executorService.shutdown();
                scheduledExecutor.shutdown();

                try {
                    // Wait for existing tasks to complete
                    if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
                        executorService.shutdownNow();
                        if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                            LOGGER.error("ExecutorService did not terminate within timeout");
                        }
                    }

                    if (!scheduledExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                        scheduledExecutor.shutdownNow();
                        if (!scheduledExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                            LOGGER.error("ScheduledExecutor did not terminate within timeout");
                        }
                    }
                } catch (InterruptedException e) {
                    executorService.shutdownNow();
                    scheduledExecutor.shutdownNow();
                    Thread.currentThread().interrupt();
                }
            }
        }

        @Override
        public String toString() {
            ensureNotClosed();

            long stamp = stampedLock.readLock();
            try {
                return String.format("ModernConcurrentMaterialStore[Size=%d, Types=%d, Value=$%.2f]",
                    size(),
                    groupByType().size(),
                    getTotalInventoryValue());
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }
    }