MaterialController.java

package com.university.bookstore.controller;

import java.util.List;
import java.util.Optional;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.university.bookstore.api.MaterialStore;
import com.university.bookstore.model.Material;
import com.university.bookstore.model.Material.MaterialType;

/**
 * REST Controller for Material management operations.
 * 
 * <p>This controller demonstrates how the existing MaterialStore interface
 * can be easily exposed as REST endpoints without modifying the core
 * business logic. It showcases the extensibility of the hexagonal
 * architecture design.</p>
 * 
 * <p>All operations delegate to the existing MaterialStore implementation,
 * maintaining the same business logic and validation rules.</p>
 * 
 * @author Navid Mohaghegh
 * @version 1.0
 * @since 2024-12-19
 */
@RestController
@RequestMapping("/api/materials")
public class MaterialController {

    private final MaterialStore materialStore;

    /**
     * Constructor with dependency injection of MaterialStore.
     * 
     * @param materialStore the material store implementation
     */
    public MaterialController(MaterialStore materialStore) {
        this.materialStore = materialStore;
    }

    /**
     * GET /api/materials - Retrieve all materials
     * 
     * @return list of all materials
     */
    @GetMapping
    public ResponseEntity<List<Material>> getAllMaterials() {
        List<Material> materials = materialStore.getAllMaterials();
        return ResponseEntity.ok(materials);
    }

    /**
     * GET /api/materials/{id} - Retrieve a specific material by ID
     * 
     * @param id the material ID
     * @return the material if found, 404 if not found
     */
    @GetMapping("/{id}")
    public ResponseEntity<Material> getMaterialById(@PathVariable("id") String id) {
        Optional<Material> material = materialStore.findById(id);
        return material.map(ResponseEntity::ok)
                      .orElse(ResponseEntity.notFound().build());
    }

    /**
     * POST /api/materials - Create a new material
     * 
     * @param material the material to create
     * @return the created material or 400 if invalid
     */
    @PostMapping
    public ResponseEntity<Material> createMaterial(@RequestBody Material material) {
        try {
            boolean added = materialStore.addMaterial(material);
            if (added) {
                return ResponseEntity.status(HttpStatus.CREATED).body(material);
            } else {
                return ResponseEntity.badRequest().build();
            }
        } catch (Exception e) {
            return ResponseEntity.badRequest().build();
        }
    }

    /**
     * PUT /api/materials/{id} - Update an existing material
     * 
     * @param id the material ID
     * @param material the updated material
     * @return the updated material or 404 if not found
     */
    @PutMapping("/{id}")
    public ResponseEntity<Material> updateMaterial(@PathVariable("id") String id, @RequestBody Material material) {
        // Remove existing material and add the updated one
        Optional<Material> existing = materialStore.removeMaterial(id);
        if (existing.isPresent()) {
            boolean added = materialStore.addMaterial(material);
            if (added) {
                return ResponseEntity.ok(material);
            } else {
                // Rollback: add the original material back
                materialStore.addMaterial(existing.get());
                return ResponseEntity.badRequest().build();
            }
        }
        return ResponseEntity.notFound().build();
    }

    /**
     * DELETE /api/materials/{id} - Delete a material
     * 
     * @param id the material ID
     * @return 204 if deleted, 404 if not found
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteMaterial(@PathVariable("id") String id) {
        Optional<Material> removed = materialStore.removeMaterial(id);
        return removed.isPresent() ? 
            ResponseEntity.noContent().build() : 
            ResponseEntity.notFound().build();
    }

    /**
     * GET /api/materials/search/title?q={query} - Search materials by title
     * 
     * @param q the search query
     * @return list of matching materials
     */
    @GetMapping("/search/title")
    public ResponseEntity<List<Material>> searchByTitle(@RequestParam("q") String q) {
        List<Material> materials = materialStore.searchByTitle(q);
        return ResponseEntity.ok(materials);
    }

    /**
     * GET /api/materials/search/creator?q={query} - Search materials by creator
     * 
     * @param q the search query
     * @return list of matching materials
     */
    @GetMapping("/search/creator")
    public ResponseEntity<List<Material>> searchByCreator(@RequestParam("q") String q) {
        List<Material> materials = materialStore.searchByCreator(q);
        return ResponseEntity.ok(materials);
    }

    /**
     * GET /api/materials/type/{type} - Get materials by type
     * 
     * @param type the material type
     * @return list of materials of the specified type
     */
    @GetMapping("/type/{type}")
    public ResponseEntity<List<Material>> getMaterialsByType(@PathVariable("type") MaterialType type) {
        List<Material> materials = materialStore.getMaterialsByType(type);
        return ResponseEntity.ok(materials);
    }

    /**
     * GET /api/materials/recent?years={years} - Get recent materials
     * 
     * @param years number of years to look back
     * @return list of recent materials
     */
    @GetMapping("/recent")
    public ResponseEntity<List<Material>> getRecentMaterials(@RequestParam(value = "years", defaultValue = "5") int years) {
        List<Material> materials = materialStore.findRecentMaterials(years);
        return ResponseEntity.ok(materials);
    }

    /**
     * Get materials by price range.
     * 
     * @param min minimum price
     * @param max maximum price
     * @return list of materials in price range
     */
    @GetMapping("/price-range")
    public ResponseEntity<List<Material>> getMaterialsByPriceRange(
            @RequestParam("min") double min, 
            @RequestParam("max") double max) {
        List<Material> materials = materialStore.getMaterialsByPriceRange(min, max);
        return ResponseEntity.ok(materials);
    }

    /**
     * GET /api/materials/stats - Get inventory statistics
     * 
     * @return inventory statistics
     */
    @GetMapping("/stats")
    public ResponseEntity<MaterialStore.InventoryStats> getInventoryStats() {
        MaterialStore.InventoryStats stats = materialStore.getInventoryStats();
        return ResponseEntity.ok(stats);
    }

    /**
     * GET /api/materials/count - Get total material count
     * 
     * @return total number of materials
     */
    @GetMapping("/count")
    public ResponseEntity<Integer> getMaterialCount() {
        int count = materialStore.size();
        return ResponseEntity.ok(count);
    }
}