package thb.jeanluc.adventure.io;

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class MusicControllerTest {

    /** Records backend calls for assertions. */
    private static final class FakeBackend implements MusicBackend {
        final List<String> calls = new ArrayList<>();
        float lastGain = Float.NaN;
        public void fadeTo(String track, float gainDb) { calls.add("fadeTo:" + track); lastGain = gainDb; }
        public void fadeOut() { calls.add("fadeOut"); }
        public void setGainDb(float gainDb) { calls.add("setGain:" + gainDb); }
        public void shutdown() { calls.add("shutdown"); }
    }

    @Test
    void firstRoomWithTrackPlaysAtDefaultLevel() {
        FakeBackend b = new FakeBackend();
        new MusicController(b).room("a");
        assertThat(b.calls).containsExactly("fadeTo:a");
    }

    @Test
    void sameTrackDoesNotRestart() {
        FakeBackend b = new FakeBackend();
        MusicController c = new MusicController(b);
        c.room("a");
        b.calls.clear();
        c.room("a");
        assertThat(b.calls).isEmpty();
    }

    @Test
    void differentTrackSwitches() {
        FakeBackend b = new FakeBackend();
        MusicController c = new MusicController(b);
        c.room("a");
        c.room("b");
        assertThat(b.calls).containsExactly("fadeTo:a", "fadeTo:b");
    }

    @Test
    void nullOrBlankRoomFadesToSilence() {
        FakeBackend b = new FakeBackend();
        MusicController c = new MusicController(b);
        c.room("a");
        c.room(null);
        c.room("  ");
        assertThat(b.calls).containsExactly("fadeTo:a", "fadeOut");
    }

    @Test
    void offStopsAndIgnoresRooms() {
        FakeBackend b = new FakeBackend();
        MusicController c = new MusicController(b);
        c.room("a");
        c.level(MusicLevel.OFF);
        b.calls.clear();
        c.room("b");
        assertThat(b.calls).isEmpty();
    }

    @Test
    void levelChangeSetsGain() {
        FakeBackend b = new FakeBackend();
        MusicController c = new MusicController(b); // default MEDIUM
        c.level(MusicLevel.HIGH);
        assertThat(b.calls).containsExactly("setGain:" + MusicLevel.HIGH.gainDb());
    }

    @Test
    void turningOffThenOnReplaysOnNextRoom() {
        FakeBackend b = new FakeBackend();
        MusicController c = new MusicController(b);
        c.room("a");
        c.level(MusicLevel.OFF);   // fadeOut
        c.level(MusicLevel.MEDIUM); // setGain
        b.calls.clear();
        c.room("a");               // current was reset → plays again
        assertThat(b.calls).containsExactly("fadeTo:a");
    }

    @Test
    void forwardsTheCurrentLevelGain() {
        FakeBackend b = new FakeBackend();
        MusicController c = new MusicController(b);
        c.room("a");
        assertThat(b.lastGain).isEqualTo(MusicLevel.MEDIUM.gainDb());
        c.level(MusicLevel.HIGH);
        c.room("b");
        assertThat(b.lastGain).isEqualTo(MusicLevel.HIGH.gainDb());
    }

    @Test
    void shutdownDelegatesToBackend() {
        FakeBackend b = new FakeBackend();
        new MusicController(b).shutdown();
        assertThat(b.calls).containsExactly("shutdown");
    }
}
