import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import org.junit.jupiter.api.Test;

// Pruebas públicas de P4 para lo que recibe lambdas. No las modifiquen.
class P4LambdasPublicTest {

    private static final LocalDate NOV_2 = LocalDate.of(2026, 11, 2);

    private final Product milk = new Product("LEC-01", "Leche evaporada", 420, 24, "Lácteos");
    private final Product soda = new Product("GAS-01", "Gaseosa 500 ml", 250, 6, "Bebidas");
    private final Product bread = new Product("PAN-01", "Pan francés", 30, 200, "Panadería");
    private final Product cheese = new Product("QUE-01", "Queso fresco", 650, 4, "Lácteos");

    private Inventory inventory() {
        Inventory inv = new Inventory();
        inv.add(milk);
        inv.add(soda);
        inv.add(bread);
        inv.add(cheese);
        return inv;
    }

    @Test
    void filterKeepsTheInsertionOrder() {
        Inventory inv = inventory();

        assertEquals(List.of(milk, cheese), inv.filter(p -> p.priceCents() > 400));
        assertEquals(List.of(), inv.filter(p -> p.stock() > 1_000));
    }

    @Test
    void filterReturnsAListThatCannotBeModified() {
        List<Product> all = inventory().filter(p -> true);

        assertThrows(UnsupportedOperationException.class, () -> all.add(milk));
    }

    @Test
    void productsCanBeSortedWithAComparatorBuiltFromLambdas() {
        Inventory inv = inventory();

        assertEquals(List.of(cheese, milk, soda, bread),
                inv.productsSortedBy(Comparator.comparingLong(Product::priceCents).reversed()));
        assertEquals(List.of(soda, milk, cheese, bread),
                inv.productsSortedBy(Comparator.comparing(Product::category).thenComparing(Product::code)));
    }

    @Test
    void aDiscountCanBeALambda() {
        Promotions promos = new Promotions();
        // Pan: desde 10 unidades, una gratis.
        promos.add((p, q) -> p.code().equals("PAN-01") && q >= 10 ? p.priceFor(1) : 0);

        assertEquals(30, promos.discountFor(bread, 12));
        assertEquals(0, promos.discountFor(bread, 9));
        assertEquals(0, promos.discountFor(milk, 12));
    }

    @Test
    void theRegisterUsesALambdaDiscountLikeAnyOther() {
        Inventory inv = inventory();
        Promotions promos = new Promotions();
        promos.add((p, q) -> p.category().equals("Lácteos") ? p.priceFor(q) / 10 : 0);
        SalesHistory history = new SalesHistory();
        Cart cart = new Cart();
        cart.add("LEC-01", 2);
        cart.add("GAS-01", 2);

        Sale sale = new Register(inv, promos, history).checkout(cart, NOV_2, "");

        assertEquals(84, sale.discountCents());
        assertEquals(756 + 500, sale.totalCents());
    }

    @Test
    void theHistoryCanBeFiltered() {
        SalesHistory history = new SalesHistory();
        Sale small = new Sale("B-000001", NOV_2, "", List.of(new SaleLine("PAN-01", "Pan francés", "Panadería", 5, 150, 0)));
        Sale big = new Sale("B-000002", NOV_2, "Rosa Quispe", List.of(new SaleLine("QUE-01", "Queso fresco", "Lácteos", 2, 1_300, 0)));
        history.add(small);
        history.add(big);

        assertEquals(List.of(big), history.filter(s -> s.totalCents() > 1_000));
        assertEquals(List.of(small), history.filter(s -> s.customer().isEmpty()));
    }
}
