// An inventory of items stored as records, with reports.

record Item(String name, String category, double price, int quantity) {}

final int LOW_STOCK = 10;

/// Returns the total value of one item's stock.
double itemValue(Item item) {
    return item.price() * item.quantity();
}

/// Returns the total value of all stock.
double totalValue(ArrayList<Item> items) {
    double total = 0.0;
    for (Item item : items) {
        total += itemValue(item);
    }
    return total;
}

/// Returns the items at or below a stock threshold, fewest first.
ArrayList<Item> lowStock(ArrayList<Item> items, int threshold) {
    ArrayList<Item> low = new ArrayList<>();
    for (Item item : items) {
        if (item.quantity() <= threshold) {
            low.add(item);
        }
    }
    low.sort(Comparator.comparing(item -> item.quantity()));
    return low;
}

/// Returns a map from each category to its items, categories in order.
Map<String, ArrayList<Item>> byCategory(ArrayList<Item> items) {
    Map<String, ArrayList<Item>> groups = new TreeMap<>();
    for (Item item : items) {
        if (!groups.containsKey(item.category())) {
            groups.put(item.category(), new ArrayList<>());
        }
        groups.get(item.category()).add(item);
    }
    return groups;
}

/// Prints the full inventory table.
void printInventory(ArrayList<Item> items) {
    ArrayList<Item> byName = new ArrayList<>(items);
    byName.sort(Comparator.comparing(item -> item.name()));

    IO.println(String.format("%-10s%-8s%6s%5s%9s",
            "Item", "Category", "Price", "Qty", "Value"));
    IO.println("-".repeat(38));
    for (Item item : byName) {
        IO.println(String.format("%-10s%-8s$%5.2f%5d  $%6.2f",
                item.name(), item.category(), item.price(),
                item.quantity(), itemValue(item)));
    }
    IO.println("-".repeat(38));
    IO.println(String.format("%-31s$%6.2f", "TOTAL", totalValue(items)));
}

/// Prints low-stock and out-of-stock warnings.
void printAlerts(ArrayList<Item> items) {
    IO.println();
    IO.println("Low stock (at or below " + LOW_STOCK + "):");
    for (Item item : lowStock(items, LOW_STOCK)) {
        int left = item.quantity();
        String state = left == 0 ? "OUT OF STOCK" : left + " left";
        IO.println(String.format("  %-12s%s", item.name(), state));
    }
}

/// Prints a summary of each category.
void printCategories(ArrayList<Item> items) {
    IO.println();
    IO.println("By category:");
    Map<String, ArrayList<Item>> groups = byCategory(items);
    for (String category : groups.keySet()) {
        ArrayList<Item> members = groups.get(category);
        double value = totalValue(members);
        IO.println(String.format("  %-10s%d items%10.2f",
                category, members.size(), value));
    }
}

void main() {
    ArrayList<Item> inventory = new ArrayList<>(List.of(
            new Item("Notebook", "paper", 3.50, 42),
            new Item("Pens", "writing", 2.25, 8),
            new Item("Stickers", "paper", 1.75, 120),
            new Item("Backpack", "bags", 34.99, 5),
            new Item("Pencils", "writing", 1.20, 3),
            new Item("Lunch box", "bags", 12.00, 0)));

    printInventory(inventory);
    printAlerts(inventory);
    printCategories(inventory);
}

// From Groundwork, chapter 10 (ch10-records), box 20.
// This example is from Groundwork, licensed CC BY-NC-SA 4.0.
// Run with: java ch10_20.java
