#include "core/config.h"
#include "core/anime.h"
#include "core/filesystem.h"
#include "core/json.h"
#include "core/strings.h"
#include "gui/translate/anime.h"
#include "gui/translate/config.h"

#include "animone/player.h"

#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <limits.h>

#include <toml11/toml.hpp>

#include <QFile>
#include <QTextStream>

#include <iostream>

/* NOTE: This config file is prone to breakage, as Minori is alpha software and
 * as such nothing is constant. */

int Config::Load()
{
	std::filesystem::path cfg_path = Filesystem::GetConfigPath();

	std::ifstream ifs(cfg_path, std::ios_base::binary);
	if (!ifs.good())
		return 0;

	toml::value data;

	try {
		data = toml::parse(ifs);
	} catch (const std::exception &ex) {
		std::cerr << "config: failed to parse toml with error " << ex.what() << std::endl;
		return 0;
	}

	service = Translate::ToService(toml::find_or(data, "General", "Service", "None"));
	locale.RefreshAvailableLocales();
	locale.SetActiveLocale(QLocale(Strings::ToQString(toml::find_or(data, "General", "Locale", "en_US"))));

	anime_list.score_format = Translate::ToScoreFormat(toml::find_or(data, "Anime List", "Score format", "100-point"));
	anime_list.language = Translate::ToLanguage(toml::find_or(data, "Anime List", "Title language", "Romaji"));
	anime_list.display_aired_episodes = toml::find_or(data, "Anime List", "Display only aired episodes", true);
	anime_list.display_available_episodes =
	    toml::find_or(data, "Anime List", "Display only available episodes in library", true);
	anime_list.highlight_anime_if_available = toml::find_or(data, "Anime List", "Highlight anime if available", true);
	anime_list.highlighted_anime_above_others =
	    (anime_list.highlight_anime_if_available)
	        ? toml::find_or(data, "Anime List", "Display highlighted anime above others", false)
	        : false;

	auth.anilist.auth_token = toml::find_or(data, "Authentication/AniList", "Auth Token", "");
	auth.anilist.user_id = toml::find_or(data, "Authentication/AniList", "User ID", 0);

	auth.kitsu.access_token = toml::find_or(data, "Authentication/Kitsu", "Access Token", "");
	auth.kitsu.access_token_expiration =
	    toml::find_or(data, "Authentication/Kitsu", "Access Token Expiration", static_cast<Time::Timestamp>(0));
	auth.kitsu.refresh_token = toml::find_or(data, "Authentication/Kitsu", "Refresh Token", "");
	auth.kitsu.user_id = toml::find_or(data, "Authentication/Kitsu", "User ID", "");

	torrents.feed_link =
	    toml::find_or(data, "Torrents", "RSS feed", "https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0");

	recognition.detect_media_players = toml::find_or(data, "Recognition", "Detect media players", true);

	{
		QFile f(":/players.animone");
		if (!f.exists())
			return false;

		f.open(QFile::ReadOnly | QFile::Text);
		QTextStream ts(&f);

		std::vector<animone::Player> players;

		if (!animone::ParsePlayersData(Strings::ToUtf8String(ts.readAll()), players))
			return false;

		recognition.players.reserve(players.size());
		for (const auto &player : players)
			recognition.players.push_back({true, player});
	}

	for (auto &[enabled, player] : recognition.players) {
		switch (player.type) {
			default:
			case animone::PlayerType::Default:
				enabled = toml::find_or(data, "Recognition/Players", player.name, true);
				break;
			case animone::PlayerType::WebBrowser:
				enabled = toml::find_or(data, "Recognition/Browsers", player.name, true);
				break;
		}
	}

	theme.SetTheme(Translate::ToTheme(toml::find_or(data, "Appearance", "Theme", "Default")));

	if (data.contains("Library") && data["Library"].contains("Folders")) {
		const toml::value &folders = toml::find(data, "Library", "Folders");
		std::vector<std::string> v = toml::get_or<std::vector<std::string>>(folders, {});
		for (const auto &s : v)
			if (!library.paths.count(s))
				library.paths.insert(s);
	}

	library.real_time_monitor = toml::find_or(data, "Library", "Real-time monitor", true);

	return 0;
}

int Config::Save()
{
	std::filesystem::path cfg_path = Filesystem::GetConfigPath();
	Filesystem::CreateDirectories(cfg_path);

	std::ofstream file(cfg_path);
	if (!file.good())
		return 0;

	toml::value data;

	data["Library"]["Folders"] = library.paths;
	data["Library"]["Real-time monitor"] = library.real_time_monitor;

	for (const auto &[enabled, player] : recognition.players) {
		const std::string section =
		    (player.type == animone::PlayerType::WebBrowser) ? "Recognition/Players" : "Recognition/Browsers";
		data[section][player.name] = enabled;
	}

	data["Recognition"]["Detect media players"] = recognition.detect_media_players;

	data["Torrents"]["RSS feed"] = torrents.feed_link;

	data["Authentication/Kitsu"]["Access Token"] = auth.kitsu.access_token;
	data["Authentication/Kitsu"]["Access Token Expiration"] = auth.kitsu.access_token_expiration;
	data["Authentication/Kitsu"]["Refresh Token"] = auth.kitsu.refresh_token;
	data["Authentication/Kitsu"]["User ID"] = auth.kitsu.user_id;

	data["Authentication/AniList"]["Auth Token"] = auth.anilist.auth_token;
	data["Authentication/AniList"]["User ID"] = auth.anilist.user_id;

	data["Anime List"]["Score format"] = Translate::ToString(anime_list.score_format);
	data["Anime List"]["Title language"] = Translate::ToString(anime_list.language);
	data["Anime List"]["Display only aired episodes"] = anime_list.display_aired_episodes;
	data["Anime List"]["Display only available episodes in library"] = anime_list.display_available_episodes;
	data["Anime List"]["Highlight anime if available"] = anime_list.highlight_anime_if_available;
	data["Anime List"]["Display highlighted anime above others"] = anime_list.highlighted_anime_above_others;

	data["Appearance"]["Theme"] = Translate::ToString(theme.GetTheme());

	data["General"]["Service"] = Translate::ToString(service);
	data["General"]["Locale"] = Strings::ToUtf8String(locale.GetLocale().name());

	file << std::setw(0) << data;

	return 0;
}
