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

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import org.junit.jupiter.api.Test;

class EventLoopTest {
    private final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    private final PrintStream out = new PrintStream(bytes, true, StandardCharsets.UTF_8);
    private final EventLoop loop = new EventLoop(out);

    private String output() {
        return bytes.toString(StandardCharsets.UTF_8);
    }

    @Test
    void dispatchesToTheRegisteredHandlerWithItsArguments() {
        List<List<String>> received = new ArrayList<>();
        loop.on("add", received::add);
        loop.run(new Scanner("add 1 2\n\nadd\n"));
        assertEquals(List.of(List.of("1", "2"), List.of()), received);
    }

    @Test
    void unknownCommandIsReportedAndTheLoopGoesOn() {
        List<String> seen = new ArrayList<>();
        loop.on("ok", args -> seen.add("ok"));
        loop.run(new Scanner("foo\nok\n"));
        assertTrue(output().startsWith("comando desconocido: foo"));
        assertEquals(List.of("ok"), seen);
    }

    @Test
    void failingHandlerDoesNotStopTheLoop() {
        List<String> seen = new ArrayList<>();
        loop.on("boom", args -> {
            throw new IllegalStateException("se rompió");
        });
        loop.on("ok", args -> seen.add("ok"));
        loop.run(new Scanner("boom\nok\n"));
        assertTrue(output().contains("error: se rompió"));
        assertEquals(List.of("ok"), seen);
    }

    @Test
    void stopEndsTheLoopBeforeTheInputEnds() {
        List<String> seen = new ArrayList<>();
        loop.on("salir", args -> loop.stop());
        loop.on("ok", args -> seen.add("ok"));
        loop.run(new Scanner("ok\nsalir\nok\n"));
        assertEquals(List.of("ok"), seen);
    }
}
