// Records.java — records, compact constructors, sealed interfaces and pattern matching (Java 21).
import java.util.List;

public class Records {

    // A record: an immutable data carrier with constructor, getters, equals, hashCode, toString
    record Money(double amount, String currency) {
        // Compact constructor: validate (and normalise) the fields
        Money {
            if (amount < 0) throw new IllegalArgumentException("Negative amount");
            currency = currency.toUpperCase();
        }

        Money plus(Money other) {
            if (!currency.equals(other.currency)) throw new IllegalArgumentException("Currency mismatch");
            return new Money(amount + other.amount, currency);   // records are immutable: return a new one
        }
    }

    // Sealed: only these three types can implement Event
    sealed interface Event permits OrderPlaced, OrderShipped, OrderCancelled { }
    record OrderPlaced(String id, Money value) implements Event { }
    record OrderShipped(String id, String tracking) implements Event { }
    record OrderCancelled(String id, String reason) implements Event { }

    // Pattern matching for switch (Java 21): record patterns + exhaustiveness
    static String describe(Event e) {
        return switch (e) {
            case OrderPlaced(String id, Money(double amt, String cur)) when amt > 1000 ->
                    id + " placed: BIG order of " + amt + " " + cur;
            case OrderPlaced(String id, Money value) -> id + " placed for " + value.amount() + " " + value.currency();
            case OrderShipped(String id, String tracking) -> id + " shipped, tracking " + tracking;
            case OrderCancelled c -> c.id() + " cancelled: " + c.reason();
        };   // no default needed: the compiler knows every case is covered
    }

    public static void main(String[] args) {
        Money a = new Money(100, "usd");
        Money b = new Money(25.5, "USD");
        System.out.println(a.plus(b));                        // Money[amount=125.5, currency=USD]
        System.out.println(a.equals(new Money(100, "USD")));  // true: value-based equality

        List<Event> events = List.of(
                new OrderPlaced("PO-1", new Money(250, "usd")),
                new OrderPlaced("PO-2", new Money(4800, "eur")),
                new OrderShipped("PO-1", "1Z999"),
                new OrderCancelled("PO-3", "customer request"));
        events.forEach(e -> System.out.println(describe(e)));
    }
}
