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

import java.util.List;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.Test;

// Pruebas públicas de P1 para Inventory. No las modifiquen.
class P1InventoryPublicTest {

    private final Product rice = new Product("ARR-02", "Arroz superior 5 kg", 2190, 8);
    private final Product milk = new Product("LEC-01", "Leche evaporada", 420, 5);
    private final Product oil = new Product("ACE-01", "Aceite vegetal 1 l", 990, 12);

    private Inventory threeProducts() {
        Inventory inv = new Inventory();
        inv.add(rice);
        inv.add(milk);
        inv.add(oil);
        return inv;
    }

    @Test
    void newInventoryIsEmpty() {
        Inventory inv = new Inventory();

        assertEquals(0, inv.size());
        assertEquals(List.of(), inv.products());
        assertEquals(0, inv.totalValueCents());
    }

    @Test
    void findsAnAddedProductByCode() {
        Inventory inv = threeProducts();

        assertTrue(inv.contains("LEC-01"));
        assertEquals("Leche evaporada", inv.find("LEC-01").name());
        assertEquals(3, inv.size());
    }

    @Test
    void anUnknownCodeIsNotContainedAndCannotBeFound() {
        Inventory inv = threeProducts();

        assertFalse(inv.contains("PAN-01"));
        assertThrows(NoSuchElementException.class, () -> inv.find("PAN-01"));
    }

    @Test
    void rejectsARepeatedCodeAndKeepsTheFirstProduct() {
        Inventory inv = threeProducts();
        Product other = new Product("LEC-01", "Leche fresca", 550, 10);

        assertThrows(IllegalArgumentException.class, () -> inv.add(other));
        assertEquals(3, inv.size());
        assertEquals("Leche evaporada", inv.find("LEC-01").name());
    }

    @Test
    void productsKeepsTheInsertionOrder() {
        Inventory inv = threeProducts();

        assertEquals(List.of(rice, milk, oil), inv.products());
    }

    @Test
    void productsCannotBeModifiedFromOutside() {
        Inventory inv = threeProducts();
        List<Product> list = inv.products();

        assertThrows(UnsupportedOperationException.class, () -> list.remove(0));
        assertEquals(3, inv.size());
    }

    @Test
    void productsIsASnapshot() {
        Inventory inv = threeProducts();
        List<Product> before = inv.products();

        inv.add(new Product("PAN-01", "Pan francés", 30, 40));

        assertEquals(3, before.size());
    }

    @Test
    void removeReturnsTheProductAndKeepsTheOrderOfTheRest() {
        Inventory inv = threeProducts();

        Product removed = inv.remove("LEC-01");

        assertEquals("LEC-01", removed.code());
        assertEquals(List.of(rice, oil), inv.products());
        assertThrows(NoSuchElementException.class, () -> inv.remove("LEC-01"));
    }

    @Test
    void addAndRemoveStockByCode() {
        Inventory inv = threeProducts();

        inv.addStock("ARR-02", 2);
        inv.removeStock("ACE-01", 12);

        assertEquals(10, inv.find("ARR-02").stock());
        assertEquals(0, inv.find("ACE-01").stock());
    }

    @Test
    void stockOperationsOnAnUnknownCodeFail() {
        Inventory inv = threeProducts();

        assertThrows(NoSuchElementException.class, () -> inv.addStock("PAN-01", 10));
        assertThrows(NoSuchElementException.class, () -> inv.removeStock("PAN-01", 1));
    }

    @Test
    void removingTooMuchStockFailsAndChangesNothing() {
        Inventory inv = threeProducts();

        assertThrows(IllegalStateException.class, () -> inv.removeStock("LEC-01", 6));
        assertEquals(5, inv.find("LEC-01").stock());
    }

    @Test
    void totalValueAddsTheValueOfEveryProduct() {
        Inventory inv = threeProducts();

        // 8 x 21.90 + 5 x 4.20 + 12 x 9.90
        assertEquals(31_500, inv.totalValueCents());
    }

    @Test
    void lowStockIncludesTheThresholdAndKeepsTheOrder() {
        Inventory inv = threeProducts();

        assertEquals(List.of(rice, milk), inv.lowStock(8));
        assertEquals(List.of(milk), inv.lowStock(5));
        assertEquals(List.of(), inv.lowStock(4));
    }

    @Test
    void lowStockRejectsANegativeThreshold() {
        Inventory inv = threeProducts();

        assertThrows(IllegalArgumentException.class, () -> inv.lowStock(-1));
    }
}
