package com.bookshop.behavioral.strategy;

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

import com.bookshop.domain.Book;
import com.bookshop.domain.Customer;
import java.math.BigDecimal;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("Strategy -- swapping pricing rules without touching the calculator")
class PriceCalculatorTest {

    private final List<Book> basket = List.of(
            new Book("978-0134757599", "Refactoring", "Martin Fowler", new BigDecimal("47.99")),
            new Book("978-0134685991", "Effective Java", "Joshua Bloch", new BigDecimal("39.99")));
    private final Customer loyalAlice = new Customer("c-1", "Alice", "alice@example.com", 4);
    private final Customer newBob = new Customer("c-2", "Bob", "bob@example.com", 0);

    @Test
    @DisplayName("the same calculator prices the same basket differently per strategy")
    void strategiesAreInterchangeable() {
        BigDecimal subtotal = new BigDecimal("87.98");

        assertEquals(subtotal,
                new PriceCalculator(Discounts.none()).totalFor(basket, newBob));
        assertEquals(new BigDecimal("80.94"),
                new PriceCalculator(Discounts.loyalty()).totalFor(basket, loyalAlice));
        assertEquals(new BigDecimal("74.78"),
                new PriceCalculator(Discounts.bulkOrder()).totalFor(basket, newBob));
    }

    @Test
    @DisplayName("each strategy is a tiny unit, testable on its own")
    void strategiesTestableInIsolation() {
        assertEquals(new BigDecimal("8.00"),
                Discounts.loyalty().discountFor(new BigDecimal("100.00"), loyalAlice));
        assertEquals(BigDecimal.ZERO,
                Discounts.bulkOrder().discountFor(new BigDecimal("49.99"), newBob));
    }

    @Test
    @DisplayName("a one-off promotion is just a lambda -- no new class ceremony")
    void oneOffStrategyAsLambda() {
        DiscountStrategy flatFiver = (subtotal, customer) -> new BigDecimal("5.00");

        BigDecimal total = new PriceCalculator(flatFiver).totalFor(basket, newBob);

        assertEquals(new BigDecimal("82.98"), total);
    }
}
