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

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

class ThermometerTest {

    /** Spy: recuerda cada lectura que recibió. */
    static class RecordingListener implements TemperatureListener {
        final List<Reading> received = new ArrayList<>();

        @Override
        public void onReading(Reading reading) {
            received.add(reading);
        }
    }

    @Test
    void everyListenerReceivesEveryReading() {
        Thermometer t = new Thermometer("refri");
        RecordingListener a = new RecordingListener();
        RecordingListener b = new RecordingListener();
        t.addListener(a);
        t.addListener(b);
        t.record(4.0);
        t.record(5.5);
        assertEquals(List.of(new Reading("refri", 4.0), new Reading("refri", 5.5)), a.received);
        assertEquals(a.received, b.received);
    }

    @Test
    void removedListenerHearsNothingMore() {
        Thermometer t = new Thermometer("refri");
        RecordingListener spy = new RecordingListener();
        t.addListener(spy);
        t.record(4.0);
        t.removeListener(spy);
        t.record(9.0);
        assertEquals(1, spy.received.size());
    }

    @Test
    void listenerCanUnsubscribeWhileBeingNotified() {
        Thermometer t = new Thermometer("refri");
        RecordingListener spy = new RecordingListener();
        t.addListener(new FirstAlert(t, 8.0));
        t.addListener(spy);
        t.addListener(new ColdChainAlarm());
        t.record(9.0);
        t.record(9.5);
        assertEquals(2, spy.received.size());
    }

    @Test
    void aFailingListenerDoesNotSilenceTheOthers() {
        Thermometer t = new Thermometer("refri");
        RuntimeException boom = new IllegalStateException("pantalla rota");
        RecordingListener spy = new RecordingListener();
        t.addListener(r -> {
            throw boom;
        });
        t.addListener(spy);
        RuntimeException thrown = assertThrows(RuntimeException.class, () -> t.record(5.0));
        assertSame(boom, thrown);
        assertEquals(1, spy.received.size());
    }

    @Test
    void alarmCountsOnlyReadingsOutOfRange() {
        Thermometer t = new Thermometer("refri");
        ColdChainAlarm alarm = new ColdChainAlarm();
        t.addListener(alarm);
        for (double c : new double[] {1.5, 2.0, 5.0, 8.0, 8.1}) {
            t.record(c);
        }
        assertEquals(2, alarm.alerts());
        assertTrue(alarm.alerts() > 0);
    }
}
