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

public class DemoOptional {
    record Libro(String codice, String titolo, String autore,
            boolean disponibile) { }

    static final class LibroAssenteException extends Exception {
        private static final long serialVersionUID = 1L;

        LibroAssenteException(String codice) {
            super("codice assente: " + codice);
        }
    }

    private static final Map<String, Libro> CATALOGO = Map.of(
            "J-01", new Libro("J-01", "Java", "Ada", true));
    private static final Map<String, Libro> CATALOGO_DI_RISERVA = Map.of(
            "R-02", new Libro("R-02", "Reti", "Grace", true));
    private static int calcoliDelDefault;

    static Optional<Libro> cerca(String codice) {
        return Optional.ofNullable(CATALOGO.get(codice));
    }

    static Optional<Libro> cercaNelCatalogoDiRiserva(String codice) {
        return Optional.ofNullable(CATALOGO_DI_RISERVA.get(codice));
    }

    static Optional<String> autore(Libro libro) {
        return Optional.ofNullable(libro.autore());
    }

    static String nomeDiDefault() {
        calcoliDelDefault++;
        return "sconosciuto";
    }

    public static void main(String[] args) {
        System.out.println(cerca("J-01").map(Libro::titolo).orElse("assente"));
        System.out.println(cerca("X").isEmpty());
        System.out.println(cerca("J-01").flatMap(DemoOptional::autore)
                .orElse("autore assente"));

        Optional<String> presente = Optional.of("Java");
        presente.orElse(nomeDiDefault());
        presente.orElseGet(DemoOptional::nomeDiDefault);
        System.out.println("default calcolati: " + calcoliDelDefault);

        System.out.println(cerca("R-02")
                .or(() -> cercaNelCatalogoDiRiserva("R-02"))
                .map(Libro::titolo).orElse("assente"));
        List<String> titoli = List.of("J-01", "X", "R-02").stream()
                .map(codice -> cerca(codice)
                        .or(() -> cercaNelCatalogoDiRiserva(codice)))
                .flatMap(Optional::stream)
                .map(Libro::titolo).toList();
        System.out.println(titoli);

        try {
            cerca("X").orElseThrow(() -> new LibroAssenteException("X"));
        } catch (LibroAssenteException errore) {
            System.out.println(errore.getMessage());
        }
    }
}
