# MaterialBundleBuilder.java

    package com.university.bookstore.builder;

    import java.util.ArrayList;
    import java.util.List;

    import com.university.bookstore.composite.MaterialBundle;
    import com.university.bookstore.composite.MaterialComponent;
    import com.university.bookstore.composite.MaterialLeaf;
    import com.university.bookstore.model.Material;

    /**
     * Builder for creating MaterialBundle instances with a fluent interface.
     * Demonstrates the Builder pattern for complex composite object construction.
     *
     * @author Navid Mohaghegh
     * @version 3.0
     * @since 2024-09-15
     */
    public class MaterialBundleBuilder implements ComponentBuilder<MaterialBundle> {

        private String bundleName;
        private double bundleDiscount = 0.0;
        private final List<MaterialComponent> components = new ArrayList<>();

        /**
         * Sets the bundle name.
         *
         * @param bundleName the bundle name
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder setBundleName(String bundleName) {
            this.bundleName = bundleName;
            return this;
        }

        /**
         * Sets the bundle discount rate.
         *
         * @param discount the discount rate (0.0 to 1.0)
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder setBundleDiscount(double discount) {
            this.bundleDiscount = Math.max(0.0, Math.min(1.0, discount));
            return this;
        }

        /**
         * Sets the bundle discount percentage.
         *
         * @param discountPercent the discount percentage (0.0 to 100.0)
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder setBundleDiscountPercent(double discountPercent) {
            return setBundleDiscount(discountPercent / 100.0);
        }

        /**
         * Adds a material to the bundle.
         *
         * @param material the material to add
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder addMaterial(Material material) {
            if (material != null) {
                components.add(new MaterialLeaf(material));
            }
            return this;
        }

        /**
         * Adds a bundle to this bundle (nested bundles).
         *
         * @param bundle the bundle to add
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder addBundle(MaterialBundle bundle) {
            if (bundle != null) {
                components.add(bundle);
            }
            return this;
        }

        /**
         * Adds a material component to the bundle.
         *
         * @param component the component to add
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder addComponent(MaterialComponent component) {
            if (component != null) {
                components.add(component);
            }
            return this;
        }

        /**
         * Adds multiple materials to the bundle.
         *
         * @param materials the materials to add
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder addMaterials(List<Material> materials) {
            if (materials != null) {
                for (Material material : materials) {
                    addMaterial(material);
                }
            }
            return this;
        }

        /**
         * Adds multiple components to the bundle.
         *
         * @param components the components to add
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder addComponents(List<MaterialComponent> components) {
            if (components != null) {
                for (MaterialComponent component : components) {
                    addComponent(component);
                }
            }
            return this;
        }

        /**
         * Removes a material from the bundle.
         *
         * @param material the material to remove
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder removeMaterial(Material material) {
            if (material != null) {
                components.removeIf(component ->
                    component instanceof MaterialLeaf &&
                    ((MaterialLeaf) component).getMaterial().equals(material));
            }
            return this;
        }

        /**
         * Removes a component from the bundle.
         *
         * @param component the component to remove
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder removeComponent(MaterialComponent component) {
            components.remove(component);
            return this;
        }

        /**
         * Clears all components from the bundle.
         *
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder clearComponents() {
            components.clear();
            return this;
        }

        /**
         * Sets a 10% discount.
         *
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder setSmallDiscount() {
            return setBundleDiscount(0.10);
        }

        /**
         * Sets a 20% discount.
         *
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder setMediumDiscount() {
            return setBundleDiscount(0.20);
        }

        /**
         * Sets a 30% discount.
         *
         * @return this builder for method chaining
         */
        public MaterialBundleBuilder setLargeDiscount() {
            return setBundleDiscount(0.30);
        }

        @Override
        public MaterialBundle build() {
            validate();

            MaterialBundle bundle = new MaterialBundle(bundleName, bundleDiscount);
            for (MaterialComponent component : components) {
                bundle.addComponent(component);
            }
            return bundle;
        }

        @Override
        public void validate() {
            if (bundleName == null || bundleName.trim().isEmpty()) {
                throw new IllegalStateException("Bundle name is required");
            }
            if (bundleDiscount < 0.0 || bundleDiscount > 1.0) {
                throw new IllegalStateException("Bundle discount must be between 0.0 and 1.0: " + bundleDiscount);
            }
        }

        @Override
        public void reset() {
            this.bundleName = null;
            this.bundleDiscount = 0.0;
            this.components.clear();
        }

        /**
         * Gets the current bundle name.
         *
         * @return the current bundle name
         */
        public String getBundleName() {
            return bundleName;
        }

        /**
         * Gets the current bundle discount.
         *
         * @return the current bundle discount
         */
        public double getBundleDiscount() {
            return bundleDiscount;
        }

        /**
         * Gets the current bundle discount percentage.
         *
         * @return the current bundle discount percentage
         */
        public double getBundleDiscountPercent() {
            return bundleDiscount * 100.0;
        }

        /**
         * Gets the current components.
         *
         * @return the current components
         */
        public List<MaterialComponent> getComponents() {
            return new ArrayList<>(components);
        }

        /**
         * Gets the number of components.
         *
         * @return the component count
         */
        public int getComponentCount() {
            return components.size();
        }

        /**
         * Checks if the bundle has any components.
         *
         * @return true if the bundle has components
         */
        public boolean hasComponents() {
            return !components.isEmpty();
        }

        /**
         * Gets the total price of all components.
         *
         * @return the total price
         */
        public double getTotalPrice() {
            return components.stream()
                .mapToDouble(MaterialComponent::getPrice)
                .sum();
        }

        /**
         * Gets the total discounted price of all components.
         *
         * @return the total discounted price
         */
        public double getTotalDiscountedPrice() {
            double totalPrice = getTotalPrice();
            return totalPrice * (1.0 - bundleDiscount);
        }

        /**
         * Gets the total savings from the bundle discount.
         *
         * @return the total savings
         */
        public double getTotalSavings() {
            return getTotalPrice() - getTotalDiscountedPrice();
        }

        @Override
        public String toString() {
            return String.format("MaterialBundleBuilder[Name=%s, Discount=%.1f%%, Components=%d, TotalPrice=$%.2f, Savings=$%.2f]",
                bundleName, getBundleDiscountPercent(), getComponentCount(), getTotalPrice(), getTotalSavings());
        }
    }