import os.path

from app.models import (
    NoteColours,
    Notes,
    Playdates,
    Playlists,
    Tracks,
)


def test_notecolours_get_colour(session):
    """Create a colour record and retrieve all colours"""

    note_colour = "#0bcdef"
    NoteColours(session, substring="substring", colour=note_colour)

    records = NoteColours.get_all(session)
    assert len(records) == 1
    record = records[0]
    assert record.colour == note_colour


def test_notecolours_get_all(session):
    """Create two colour records and retrieve them all"""

    note1_colour = "#1bcdef"
    note2_colour = "#20ff00"
    NoteColours(session, substring="note1", colour=note1_colour)
    NoteColours(session, substring="note2", colour=note2_colour)

    records = NoteColours.get_all(session)
    assert len(records) == 2
    assert note1_colour in [n.colour for n in records]
    assert note2_colour in [n.colour for n in records]


def test_notecolours_get_colour_none(session):
    note_colour = "#3bcdef"
    NoteColours(session, substring="substring", colour=note_colour)

    result = NoteColours.get_colour(session, "xyz")
    assert result is None


def test_notecolours_get_colour_match(session):
    note_colour = "#4bcdef"
    nc = NoteColours(session, substring="sub", colour=note_colour)
    assert nc

    result = NoteColours.get_colour(session, "The substring")
    assert result == note_colour


def test_notes_creation(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    note_text = "note text"
    note = Notes(session, playlist.id, 0, note_text)
    assert note

    notes = session.query(Notes).all()
    assert len(notes) == 1
    assert notes[0].note == note_text


def test_notes_delete(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    note_text = "note text"
    note = Notes(session, playlist.id, 0, note_text)
    assert note

    notes = session.query(Notes).all()
    assert len(notes) == 1
    assert notes[0].note == note_text

    note.delete_note(session)
    notes = session.query(Notes).all()
    assert len(notes) == 0


def test_notes_update_row_only(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    note_text = "note text"
    note = Notes(session, playlist.id, 0, note_text)
    new_row = 10

    note.update_note(session, new_row)

    notes = session.query(Notes).all()
    assert len(notes) == 1
    assert notes[0].row == new_row


def test_notes_update_text(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    note_text = "note text"
    note = Notes(session, playlist.id, 0, note_text)
    new_text = "This is new"
    new_row = 0

    note.update_note(session, new_row, new_text)
    notes = session.query(Notes).all()

    assert len(notes) == 1
    assert notes[0].note == new_text
    assert notes[0].row == new_row


def test_playdates_add_playdate(session):
    """Test playdate and last_played retrieval"""

    # We need a track
    track_path = "/a/b/c"
    track = Tracks(session, track_path)

    playdate = Playdates(session, track.id)
    assert playdate

    last_played = Playdates.last_played(session, track.id)
    assert abs((playdate.lastplayed - last_played).total_seconds()) < 2


def test_playdates_remove_track(session):
    """Test removing a track from a playdate"""

    # We need a track
    track_path = "/a/b/c"
    track = Tracks(session, track_path)

    playdate = Playdates(session, track.id)
    Playdates.remove_track(session, track.id)

    last_played = Playdates.last_played(session, track.id)
    assert last_played is None


def test_playlist_create(session):
    playlist = Playlists(session, "my playlist")
    assert playlist


def test_playlist_add_note(session):
    note_text = "my note"
    note_row = 2

    playlist = Playlists(session, "my playlist")
    note = playlist.add_note(session, note_row, note_text)

    assert len(playlist.notes) == 1
    playlist_note = playlist.notes[0]
    assert playlist_note.note == note_text


def test_playlist_add_track(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    # We need a track
    track_path = "/a/b/c"
    track = Tracks(session, track_path)

    row = 17

    playlist.add_track(session, track.id, row)

    assert len(playlist.tracks) == 1
    playlist_track = playlist.tracks[row]
    assert playlist_track.path == track_path


def test_playlist_tracks(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    # We need two tracks
    track1_path = "/a/b/c"
    track1_row = 17
    track1 = Tracks(session, track1_path)

    track2_path = "/x/y/z"
    track2_row = 29
    track2 = Tracks(session, track2_path)

    playlist.add_track(session, track1.id, track1_row)
    playlist.add_track(session, track2.id, track2_row)

    tracks = playlist.tracks
    assert tracks[track1_row] == track1
    assert tracks[track2_row] == track2


def test_playlist_notes(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    # We need two notes
    note1_text = "note1 text"
    note1_row = 11
    _ = Notes(session, playlist.id, note1_row, note1_text)

    note2_text = "note2 text"
    note2_row = 19
    _ = Notes(session, playlist.id, note2_row, note2_text)

    notes = playlist.notes
    assert note1_text in [n.note for n in notes]
    assert note1_row in [n.row for n in notes]
    assert note2_text in [n.note for n in notes]
    assert note2_row in [n.row for n in notes]


def test_playlist_open_and_close(session):
    # We need a playlist
    playlist = Playlists(session, "my playlist")

    assert len(Playlists.get_open(session)) == 1
    assert len(Playlists.get_closed(session)) == 0

    playlist.close(session)

    assert len(Playlists.get_open(session)) == 0
    assert len(Playlists.get_closed(session)) == 1

    playlist.mark_open(session)

    assert len(Playlists.get_open(session)) == 1
    assert len(Playlists.get_closed(session)) == 0


def test_playlist_get_all_and_by_id(session):
    # We need two playlists
    p1_name = "playlist one"
    p2_name = "playlist two"
    playlist1 = Playlists(session, p1_name)
    _ = Playlists(session, p2_name)

    all_playlists = Playlists.get_all(session)
    assert len(all_playlists) == 2
    assert p1_name in [p.name for p in all_playlists]
    assert p2_name in [p.name for p in all_playlists]
    assert Playlists.get_by_id(session, playlist1.id).name == p1_name


def test_playlist_remove_tracks(session):
    # Need two playlists and three tracks
    p1_name = "playlist one"
    playlist1 = Playlists(session, p1_name)
    p2_name = "playlist two"
    playlist2 = Playlists(session, p2_name)

    track1_path = "/a/b/c"
    track1 = Tracks(session, track1_path)
    track2_path = "/m/n/o"
    track2 = Tracks(session, track2_path)
    track3_path = "/x/y/z"
    track3 = Tracks(session, track3_path)

    # Add all tracks to both playlists
    for p in [playlist1, playlist2]:
        for t in [track1, track2, track3]:
            p.add_track(session, t.id)

    assert len(playlist1.tracks) == 3
    assert len(playlist2.tracks) == 3

    playlist1.remove_track(session, 1)
    assert len(playlist1.tracks) == 2
    # Check the track itself still exists
    original_track = Tracks.get_by_id(session, track1.id)
    assert original_track

    playlist1.remove_all_tracks(session)
    assert len(playlist1.tracks) == 0
    assert len(playlist2.tracks) == 3


def test_playlist_get_track_playlists(session):
    # Need two playlists and two tracks
    p1_name = "playlist one"
    playlist1 = Playlists(session, p1_name)
    p2_name = "playlist two"
    playlist2 = Playlists(session, p2_name)

    track1_path = "/a/b/c"
    track1 = Tracks(session, track1_path)
    track2_path = "/m/n/o"
    track2 = Tracks(session, track2_path)

    # Put track1 in both playlists, track2 only in playlist1
    playlist1.add_track(session, track1.id)
    playlist2.add_track(session, track1.id)
    playlist1.add_track(session, track2.id)

    playlists_track1 = track1.playlists
    playlists_track2 = track2.playlists
    assert p1_name in [a.playlist.name for a in playlists_track1]
    assert p2_name in [a.playlist.name for a in playlists_track1]
    assert p1_name in [a.playlist.name for a in playlists_track2]
    assert p2_name not in [a.playlist.name for a in playlists_track2]


def test_playlist_move_track(session):
    # We need two playlists
    p1_name = "playlist one"
    p2_name = "playlist two"
    playlist1 = Playlists(session, p1_name)
    playlist2 = Playlists(session, p2_name)

    # Need two tracks
    track1_row = 17
    track1_path = "/a/b/c"
    track1 = Tracks(session, track1_path)
    track2_row = 29
    track2_path = "/m/n/o"
    track2 = Tracks(session, track2_path)

    # Add both to playlist1 and check
    playlist1.add_track(session, track1.id, track1_row)
    playlist1.add_track(session, track2.id, track2_row)

    tracks = playlist1.tracks
    assert tracks[track1_row] == track1
    assert tracks[track2_row] == track2

    # Move track2 to playlist2 and check
    playlist1.move_track(session, [track2_row], playlist2)

    tracks1 = playlist1.tracks
    tracks2 = playlist2.tracks
    assert len(tracks1) == 1
    assert len(tracks2) == 1
    assert tracks1[track1_row] == track1
    assert tracks2[0] == track2


def test_tracks_get_all_paths(session):
    # Need two tracks
    track1_path = "/a/b/c"
    _ = Tracks(session, track1_path)
    track2_path = "/m/n/o"
    _ = Tracks(session, track2_path)

    result = Tracks.get_all_paths(session)
    assert track1_path in result
    assert track2_path in result


def test_tracks_get_all_tracks(session):
    # Need two tracks
    track1_path = "/a/b/c"
    track1 = Tracks(session, track1_path)
    track2_path = "/m/n/o"
    track2 = Tracks(session, track2_path)

    result = Tracks.get_all_tracks(session)
    assert track1_path in [a.path for a in result]
    assert track2_path in [a.path for a in result]


def test_tracks_by_filename(session):
    track1_path = "/a/b/c"

    track1 = Tracks(session, track1_path)
    assert Tracks.get_by_filename(
        session, os.path.basename(track1_path)
    ) is track1


def test_tracks_by_path(session):
    track1_path = "/a/b/c"

    track1 = Tracks(session, track1_path)
    assert Tracks.get_by_path(session, track1_path) is track1


def test_tracks_by_id(session):
    track1_path = "/a/b/c"

    track1 = Tracks(session, track1_path)
    assert Tracks.get_by_id(session, track1.id) is track1


def test_tracks_rescan(session):
    # Get test track
    test_track_path = "./testdata/isa.mp3"
    test_track_data = "./testdata/isa.py"

    track = Tracks(session, test_track_path)
    track.rescan(session)

    # Get test data
    with open(test_track_data) as f:
        testdata = eval(f.read())

    # Re-read the track
    track_read = Tracks.get_by_path(session, test_track_path)

    assert track_read.duration == testdata['duration']
    assert track_read.start_gap == testdata['leading_silence']
    # Silence detection can vary, so ± 1 second is OK
    assert track_read.fade_at < testdata['fade_at'] + 1000
    assert track_read.fade_at > testdata['fade_at'] - 1000
    assert track_read.silence_at < testdata['trailing_silence'] + 1000
    assert track_read.silence_at > testdata['trailing_silence'] - 1000


def test_tracks_remove_by_path(session):
    track1_path = "/a/b/c"

    track1 = Tracks(session, track1_path)
    assert len(Tracks.get_all_tracks(session)) == 1
    Tracks.remove_by_path(session, track1_path)
    assert len(Tracks.get_all_tracks(session)) == 0


def test_tracks_search_artists(session):

    track1_path = "/a/b/c"
    track1_artist = "Artist One"
    track1 = Tracks(session, track1_path)
    track1.artist = track1_artist

    track2_path = "/m/n/o"
    track2_artist = "Artist Two"
    track2 = Tracks(session, track2_path)
    track2.artist = track2_artist

    session.commit()

    x = Tracks.get_all_tracks(session)
    artist_first_word = track1_artist.split()[0].lower()
    assert len(Tracks.search_artists(session, artist_first_word)) == 2
    assert len(Tracks.search_artists(session, track1_artist)) == 1


def test_tracks_search_titles(session):
    track1_path = "/a/b/c"
    track1_title = "Title One"
    track1 = Tracks(session, track1_path)
    track1.title = track1_title

    track2_path = "/m/n/o"
    track2_title = "Title Two"
    track2 = Tracks(session, track2_path)
    track2.title = track2_title

    session.commit()

    x = Tracks.get_all_tracks(session)
    title_first_word = track1_title.split()[0].lower()
    assert len(Tracks.search_titles(session, title_first_word)) == 2
    assert len(Tracks.search_titles(session, track1_title)) == 1


def test_tracks_update_lastplayed(session):
    track1_path = "/a/b/c"
    track1 = Tracks(session, track1_path)

    assert track1.lastplayed is None
    track1.update_lastplayed(session, track1.id)
    assert track1.lastplayed is not None


def test_tracks_update_info(session):
    path = "/a/b/c"
    artist = "The Beatles"
    title = "Help!"
    newinfo = "abcdef"

    track1 = Tracks(session, path)
    track1.artist = artist
    track1.title = title

    test1 = Tracks.get_by_id(session, track1.id)
    assert test1.artist == artist
    assert test1.title == title
    assert test1.path == path

    track1.path = newinfo
    test2 = Tracks.get_by_id(session, track1.id)
    assert test2.artist == artist
    assert test2.title == title
    assert test2.path == newinfo

    track1.artist = newinfo
    test2 = Tracks.get_by_id(session, track1.id)
    assert test2.artist == newinfo
    assert test2.title == title
    assert test2.path == newinfo

    track1.title = newinfo
    test3 = Tracks.get_by_id(session, track1.id)
    assert test3.artist == newinfo
    assert test3.title == newinfo
    assert test3.path == newinfo
