#include <memory>
#include <optional>
#include <random>
#include <string>

#include <QApplication>
#include <QDir>

#include "ConfigLoader.h"
#include "ConsoleLogger.h"
#include "logging.h"
#include "LogManager.h"
#include "MainWindow.h"
#include "ReplayReader.h"
#include "ReplayRecorder.h"
#include "Simulation.h"

int main(int argc, char *argv[])
{
	LogManager::getInstance()->addLogger(std::make_shared<ConsoleLogger>());
	LogManager::getInstance()->setLoggingEnabled(true);
	LOG_INFO("Logging enabled");

	QApplication::setApplicationName("DotaFactory");

	if (QSysInfo::windowsVersion() != QSysInfo::WV_None)
	{
		QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
	}

	QApplication application(argc, argv);

	const QDir dataDir("data");
	if (!dataDir.exists())
	{
		QDir().mkdir(dataDir.dirName());
	}

	// Optional "--replay <file>" launches view-only playback of a recorded run.
	std::optional<std::string> replayPath;
	for (int i = 1; i + 1 < argc; ++i)
	{
		if (std::string(argv[i]) == "--replay")
		{
			replayPath = argv[i + 1];
			break;
		}
	}

	GameConfig config = ConfigLoader::loadFromDirectory(CONFIG_DIR);

	unsigned int seed = 0;
	std::shared_ptr<ParsedReplay> replay;
	if (replayPath.has_value())
	{
		std::optional<ParsedReplay> parsed = readReplayFile(*replayPath);
		if (!parsed.has_value())
		{
			LOG_ERROR("Failed to read replay file: " + *replayPath);
			return 1;
		}
		// Warn (but proceed) on identity mismatches: a different config or build
		// can desync playback (see docs/replay_design.md).
		if (parsed->header.version != 1)
		{
			LOG_WARNING_STREAM(<< "Replay format version " << parsed->header.version
			                   << " differs from 1; playback may fail");
		}
		if (computeReplayConfigHash(CONFIG_DIR) != parsed->header.configHash)
		{
			LOG_WARNING("Replay config hash mismatch; playback may desync");
		}
		seed   = parsed->header.seed;
		replay = std::make_shared<ParsedReplay>(std::move(*parsed));
	}
	else
	{
		// Random seed generated outside the sim so the Simulation stays a pure
		// function of (seed, config, commands); written to the replay header
		// (see docs/replay_design.md "Seed and config").
		seed = std::random_device{}();
	}

	std::unique_ptr<Simulation> sim = std::make_unique<Simulation>(std::move(config), seed);

	MainWindow window(sim.get(), std::string(CONFIG_DIR), replay);
	window.show();

	const int ret = application.exec();

	return ret;
}
