import pytest

from solo_tool.solo_tool import SoloTool
from player_mock import Player as MockPlayer

@pytest.fixture
def mockPlayer():
    return MockPlayer()

@pytest.fixture
def uut(mockPlayer):
    return SoloTool(player=mockPlayer)

@pytest.fixture
def testSongs():
    return [
        "test.flac",
        "test.mp3"
    ]

def test_songSelectionFlow(uut, mockPlayer, testSongs):
    # Initially, song list is empty and no song is selected
    assert uut.song is None
    assert mockPlayer.currentSong == None
    assert uut.songs == []

    # When the first song is added, it is selected automatically
    uut.addSong(testSongs[0])
    assert uut.song == 0
    assert mockPlayer.currentSong == testSongs[0]
    assert uut.songs == testSongs[0:1]

    # Subsequently added songs are not selected automatically
    # Song list order is addition order
    for i, song in enumerate(testSongs[1:]):
        uut.addSong(testSongs[1])
        assert uut.song == 0
        assert mockPlayer.currentSong == testSongs[0]
        assert uut.songs == testSongs[0:i + 2]

    # Songs are selected by index
    for i, s in enumerate(uut.songs):
        uut.song = i
        assert uut.song == i
        assert mockPlayer.currentSong == uut.songs[i]

def test_songSelectionEdgeCases(uut, mockPlayer, testSongs):
    # When no songs are available, selecting has no effect
    uut.song = 0
    assert uut.song == None
    assert mockPlayer.currentSong == None

    for song in testSongs:
        uut.addSong(song)

    # The current song cannot be de-selected
    uut.song = None
    assert uut.song == 0
    assert mockPlayer.currentSong == testSongs[0]

    # Non-existent songs cannot be selected
    uut.song = -1
    assert uut.song == 0
    assert mockPlayer.currentSong == testSongs[0]

    uut.song = len(testSongs)
    assert uut.song == 0
    assert mockPlayer.currentSong == testSongs[0]

def test_songAdditionEdgeCases(uut, mockPlayer, testSongs):
    for song in testSongs:
        uut.addSong(song)

    # Modifying the song list directly has no effect
    uut.songs.append("something")
    assert uut.songs == testSongs
    assert mockPlayer.currentSong == testSongs[0]

    # Same song cannot be added twice
    uut.addSong(testSongs[0])
    assert uut.song == 0
    assert mockPlayer.currentSong == testSongs[0]
    assert uut.songs == testSongs

    # Songs must exist in the filesystem
    with pytest.raises(FileNotFoundError):
        uut.addSong("/not/a/real/file")
    assert uut.song == 0
    assert mockPlayer.currentSong == testSongs[0]
    assert uut.songs == testSongs

def test_songSelectionNotification(uut, testSongs):
    selectionCalled = False
    selectionValue = None
    def selectionCallback(value):
        nonlocal selectionCalled, selectionValue
        selectionCalled = True
        selectionValue = value

    uut.registerSongSelectionCallback(selectionCallback)
    assert not selectionCalled

    # Adding the first song triggers because the song is automatically selected
    uut.addSong(testSongs[0])

    assert selectionCalled
    assert selectionValue == 0
    selectionCalled = False

    # Adding more songs does not trigger
    for i, song in enumerate(testSongs[1:]):
        uut.addSong(song)
        assert not selectionCalled

    # Selecting another song triggers
    uut.song = 1
    assert selectionCalled
    assert selectionValue == 1
    selectionCalled = False

    # Selecting the currently selected song does not trigger
    uut.song = 1
    assert not selectionCalled

def test_songListNotification(uut, testSongs):
    listCalled = False
    listValue = None
    def listCallback(value):
        nonlocal listCalled, listValue
        listCalled = True
        listValue = value

    uut.registerSongListCallback(listCallback)
    assert not listCalled

    # Adding the first song triggers
    uut.addSong(testSongs[0])

    assert listCalled
    assert listValue == testSongs[0:1]
    listCalled = False

    # Adding more songs triggers
    for i, song in enumerate(testSongs[1:]):
        uut.addSong(song)

        assert listCalled
        assert listValue == testSongs[0:i + 2]
        listCalled = False

    # Modifying the list in place does not trigger
    uut.songs.append("something")
    assert not listCalled

    # Adding an existing song does not trigger
    uut.addSong(testSongs[0])
    assert not listCalled