/* Copyright 2020, Michele Santullo
 * This file is part of orotool.
 *
 * Orotool 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.
 *
 * Orotool 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 Orotool.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "oro/api.hpp"
#include "oro/originsdb.hpp"
#include "orotool_config.hpp"
#include "evloop.hpp"
#include "app_config.hpp"
#include "duckhandy/int_conv.hpp"
#include "duckhandy/string_bt.hpp"
#if !defined(NDEBUG)
#	include <iostream>
#endif
#if defined(OROTOOL_WITH_RESTCCPP)
#	include <restc-cpp/error.h>
#endif
#include <regex>

namespace {
	void print_ping(oro::Api& oro_api) {
		auto ping = oro_api.ping(false);

#if !defined(NDEBUG)
		std::cout << "date: " << ping.header.date << '\n';
		std::cout << "rate limit: " << ping.header.rate_limit << '\n';
		std::cout << "remaining: " << ping.header.rate_limit_remaining << '\n';
		std::cout << "reset: " << ping.header.rate_limit_reset << '\n';
		std::cout << "retry after: " << ping.header.retry_after << '\n';
		std::cout << "server: " << ping.header.server << '\n';
		std::cout << "-----\n";
		std::cout << "timestamp: " << ping.data.generation_timestamp << '\n';
		std::cout << "answer: " << ping.data.message << '\n';
		std::cout << "version: " << ping.data.version << '\n';
#else
		static_cast<void>(ping);
#endif
	}

	constexpr auto app_version() {
		constexpr const auto dot = dhandy::bt::make_string(".");
		constexpr const auto maj = dhandy::int_to_ary(duck::g_version_major);
		constexpr const auto min = dhandy::int_to_ary(duck::g_version_minor);
		constexpr const auto pat = dhandy::int_to_ary(duck::g_version_patch);

		constexpr const dhandy::bt::string<maj.size(), char> maj_n(maj.data());
		constexpr const dhandy::bt::string<min.size(), char> min_n(min.data());
		constexpr const dhandy::bt::string<pat.size(), char> pat_n(pat.data());

		return (maj_n + dot + min_n + dot + pat_n);
	}

	constexpr auto app_api_name() {
		constexpr const auto name = dhandy::bt::make_string(duck::g_project_name);
		constexpr const auto us = dhandy::bt::make_string("_");
		return (name + us + app_version());
	}

	void validate_key(std::string_view key) {
		using std::string;
		using std::logic_error;

		std::regex key_reg("^[a-z0-9]{32}$");
		if (not std::regex_match(key.begin(), key.end(), key_reg))
			throw logic_error("API key \"" + string(key) + "\" doesn't look valid");

		return;
	}
} //unnamed namespace

int main(int argc, char* argv[]) {
	try {
		duck::AppConfig app_conf;
		std::string_view api_key = (2 == argc ? std::string_view(argv[1]) : app_conf.api_key());
		auto oro_api = oro::make_api(
			duck::g_base_url,
			std::string(api_key),
			app_api_name().data(),
			duck::g_build_purpose
		);

		print_ping(*oro_api);
		validate_key(app_conf.api_key());

		std::unique_ptr<oro::OriginsDB> db(oro::OriginsDB::make(app_conf.backend(), app_conf.db_path()));

		duck::test(oro_api.get(), db.get(), app_conf);
	}
#if defined(OROTOOL_WITH_RESTCCPP)
	catch (const restc_cpp::CommunicationException& err) {
		std::cerr << "Communication with server \"" << duck::g_base_url <<
			"\" failed: " << err.what() << '\n';
		return 1;
	}
#endif
	catch (const std::exception& err) {
		std::cerr << "An error occurred during the program execution: " <<
			err.what() << '\n';
		return 1;
	}

/*
	{
		auto whoami = oro_api.who_am_i();
		std::cout << "master id: " << whoami.second.master_id << '\n';
	}

	{
		auto items = oro_api.items_list();
		std::cout << "Total items in DB: " << items.second.items.size() << '\n';
	}

	{
		auto icons = oro_api.items_icons();
		std::cout << "Total icons in DB: " << icons.second.icons.size() << '\n';
	}

	{
		auto shops = oro_api.market_list();
		for (const auto& shop : shops.second.shops) {
			std::cout << "Shop \"" << shop.title << "\" by " << shop.owner <<
				" in " << shop.location.map <<
				" at <" << shop.location.x << ", " << shop.location.y << "> ";
			if (shop.type == oro::ShopType::Buying)
				std::cout << "buying ";
			else
				std::cout << "selling ";
			std::cout << shop.items.size() << " items\n";
		}
	}

	{
		auto fame = oro_api.fame_list();
		std::cout << "Forgers: ";
		for (const auto& forger : fame.second.forgers) {
			std::cout << forger.name << ", ";
		}
		std::cout << '\n';
		std::cout << "Brewers: ";
		for (const auto& brewer : fame.second.brewers) {
			std::cout << brewer.name << ", ";
		}
		std::cout << '\n';
	}
*/

	return 0;
}
