/*
 * Copyright (C) 2011-2024 Michael Lugmair
 *
 * This file is part of sayonara player
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "test/Common/SayonaraTest.h"
#include "test/Common/PlayManagerMock.h"
#include "test/Playlist/PlaylistTestUtils.h"

#include "Components/Playlist/LocalPathPlaylistCreator.h"
#include "Components/Playlist/Playlist.h"
#include "Components/Playlist/PlaylistInterface.h"
#include "Components/Playlist/PlaylistModifiers.h"
#include "Utils/MetaData/MetaDataList.h"
#include "Utils/Parser/M3UParser.h"
#include "Utils/FileSystem.h"

#include "Utils/Algorithm.h"
#include "Utils/Logger/Logger.h"

#include <QSignalSpy>

#include <functional> // bad_function_call

// access working directory with Test::Base::tempPath("somefile.txt");

using Playlist::LocalPathPlaylistCreator;

class PlaylistCreatorMock :
	public Playlist::Creator
{
	public:
		PlaylistCreatorMock() :
			m_playManager {Test::PlayManagerMock {}} {}

		~PlaylistCreatorMock() override = default;

		PlaylistPtr playlist(int playlistIndex) override { return m_playlists[playlistIndex]; }

		[[nodiscard]] QString
		requestNewPlaylistName(const QString& /*prefix*/) const override
		{
			return QString("%1").arg(m_playlists.count() + 1);
		}

		int createPlaylist(const MetaDataList& /*tracks*/, const QString& name,
		                   bool /*temporary*/, bool /*isLocked*/) override
		{
			int index = m_playlists.count();
			auto playlist =
				std::make_shared<::Playlist::Playlist>(index, name, &m_playManager, Util::FileSystem::create());

			playlist->setId(index);
			m_playlists << playlist;

			return index;
		}

		PlaylistPtr playlistById(int /*playlistId*/) override { throw std::bad_function_call {}; }

		int createPlaylist(const QStringList& /*pathList*/, const QString& /*name*/, bool /*temporary*/,
		                   LocalPathPlaylistCreator* /*creator*/) override
		{
			throw std::bad_function_call {};
		}

		int createPlaylist(const CustomPlaylist& /*customPlaylist*/) override { throw std::bad_function_call {}; }

		int createEmptyPlaylist(bool /*override*/) override { throw std::bad_function_call {}; }

		int createCommandLinePlaylist(const QStringList& /*pathList*/,
		                              LocalPathPlaylistCreator* /*creator*/) override
		{
			throw std::bad_function_call {};
		}

		[[nodiscard]] int count() const
		{
			return m_playlists.count();
		}

		[[nodiscard]] QList<PlaylistPtr> playlists() const
		{
			return m_playlists;
		}

	private:
		Test::PlayManagerMock m_playManager;
		QList<PlaylistPtr> m_playlists;
};

class LocalPathPlaylistCreatorTest :
	public Test::Base
{
	Q_OBJECT

	public:
		LocalPathPlaylistCreatorTest() :
			Base("PlaylistFromPathCreatorTest"),
			m_pathTrackMap {Test::Playlist::createTrackFiles(tempPath())} {}

	private slots:
		[[maybe_unused]] void testSinglePlaylist()
		{
			auto playlistCreator = PlaylistCreatorMock();
			auto* playlistFromPathCreator = LocalPathPlaylistCreator::create(&playlistCreator);

			auto pathList = QStringList {};

			for(const auto& [filepath, track]: m_pathTrackMap)
			{
				pathList << filepath;
			}

			playlistFromPathCreator->createPlaylists({tempPath()}, "Some Playlist", true);

			wait(playlistFromPathCreator);

			QVERIFY(playlistCreator.count() == 1);

			const auto playlist = playlistCreator.playlists()[0];
			QVERIFY(Playlist::count(*playlist) == m_pathTrackMap.count());
		}

		[[maybe_unused]] void testPlaylistWithPlaylistFile()
		{
			auto playlistCreator = PlaylistCreatorMock();
			auto* playlistFromPathCreator = LocalPathPlaylistCreator::create(&playlistCreator);

			auto pathList = QStringList {};
			auto tracks = MetaDataList {};

			for(const auto& [filepath, track]: m_pathTrackMap)
			{
				pathList << filepath;
				tracks << track;
			}

			constexpr const auto indexToRemove = 5;
			tracks.removeTracks(indexToRemove, tracks.count() - 1);

			M3UParser::saveM3UPlaylist(tempPath("bla.m3u"), tracks, false);

			const auto paths = QStringList {pathList.first(), tempPath("bla.m3u")};
			playlistFromPathCreator->createPlaylists(paths, "Some Playlist", true);

			wait(playlistFromPathCreator);

			const auto playlist = playlistCreator.playlists()[1];
			QVERIFY(playlistCreator.count() == 2);
			QVERIFY(Playlist::count(*playlist) == 5);
		}

	private: // NOLINT(readability-redundant-access-specifiers)
		static void wait(const LocalPathPlaylistCreator* creator)
		{
			auto spy = QSignalSpy(creator, &LocalPathPlaylistCreator::sigAllPlaylistsCreated);

			QVERIFY(spy.wait(5));
			QCOMPARE(spy.count(), 1);
		}

		const Test::Playlist::PathTrackMap m_pathTrackMap;
};

QTEST_GUILESS_MAIN(LocalPathPlaylistCreatorTest)

#include "LocalPathPlaylistCreatorTest.moc"
