#include "AppConfigProvider.h"
#include "DeckLinkDisplayMode.h"

#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

namespace
{
int gFailures = 0;

void Expect(bool condition, const std::string& message)
{
	if (condition)
		return;

	++gFailures;
	std::cerr << "FAILED: " << message << "\n";
}

std::filesystem::path WriteConfigFixture()
{
	const std::filesystem::path path = std::filesystem::temp_directory_path() / "render-cadence-compositor-config-test.json";
	std::ofstream output(path, std::ios::binary);
	output
		<< "{\n"
		<< "  \"shaderLibrary\": \"test-shaders\",\n"
		<< "  \"serverPort\": 8181,\n"
		<< "  \"oscBindAddress\": \"127.0.0.1\",\n"
		<< "  \"oscPort\": 9100,\n"
		<< "  \"oscSmoothing\": 0.25,\n"
		<< "  \"inputVideoFormat\": \"720p\",\n"
		<< "  \"inputFrameRate\": \"50\",\n"
		<< "  \"outputVideoFormat\": \"2160p\",\n"
		<< "  \"outputFrameRate\": \"60\",\n"
		<< "  \"autoReload\": false,\n"
		<< "  \"maxTemporalHistoryFrames\": 8,\n"
		<< "  \"previewEnabled\": true,\n"
		<< "  \"previewFps\": 24,\n"
		<< "  \"enableExternalKeying\": true\n"
		<< "}\n";
	return path;
}

void TestLoadsRuntimeHostConfig()
{
	using namespace RenderCadenceCompositor;

	const std::filesystem::path path = WriteConfigFixture();
	AppConfigProvider provider;
	std::string error;
	const bool loaded = provider.Load(path, error);
	const AppConfig& config = provider.Config();

	Expect(loaded, "config loads");
	Expect(error.empty(), "config load has no error");
	Expect(provider.LoadedFromFile(), "provider records file load");
	Expect(config.shaderLibrary == "test-shaders", "shader library loads");
	Expect(config.http.preferredPort == 8181, "server port loads");
	Expect(config.oscBindAddress == "127.0.0.1", "OSC bind address loads");
	Expect(config.oscPort == 9100, "OSC port loads");
	Expect(config.oscSmoothing == 0.25, "OSC smoothing loads");
	Expect(config.inputVideoFormat == "720p", "input format loads");
	Expect(config.inputFrameRate == "50", "input frame rate loads");
	Expect(config.outputVideoFormat == "2160p", "output format loads");
	Expect(config.outputFrameRate == "60", "output frame rate loads");
	Expect(!config.autoReload, "auto reload loads");
	Expect(config.maxTemporalHistoryFrames == 8, "history length loads");
	Expect(config.previewEnabled, "preview enabled toggle loads");
	Expect(config.previewFps == 24.0, "preview fps loads");
	Expect(config.deckLink.externalKeyingEnabled, "external keying loads");

	std::filesystem::remove(path);
}

void TestCommandLineOverrides()
{
	using namespace RenderCadenceCompositor;

	AppConfigProvider provider;
	const char* argv[] = {
		"app.exe",
		"--shader",
		"solid-color",
		"--port",
		"8282"
	};
	provider.ApplyCommandLine(5, const_cast<char**>(argv));

	const AppConfig& config = provider.Config();
	Expect(config.runtimeShaderId == "solid-color", "shader CLI override applies");
	Expect(config.http.preferredPort == 8282, "port CLI override applies");
}

void TestPreviewDefaultsAreOptIn()
{
	using namespace RenderCadenceCompositor;

	const AppConfig config = DefaultAppConfig();
	Expect(!config.previewEnabled, "preview is disabled by default");
	Expect(config.previewFps == 60.0, "preview fps default is 60");
}

void TestHelpers()
{
	using namespace RenderCadenceCompositor;

	unsigned width = 0;
	unsigned height = 0;
	VideoFormatDimensions("720p", width, height);
	Expect(width == 1280 && height == 720, "720p dimensions resolve");

	VideoFormatDimensions("2160p", width, height);
	Expect(width == 3840 && height == 2160, "2160p dimensions resolve");

	const double duration = FrameDurationMillisecondsFromRateString("50");
	Expect(duration > 19.9 && duration < 20.1, "frame duration parses numeric rate");
	const double deckLinkDuration = FrameDurationMillisecondsFromDisplayMode(bmdModeHD1080p5994, 0.0);
	Expect(deckLinkDuration > 16.6833 && deckLinkDuration < 16.6834, "DeckLink 59.94 display mode duration is exact");

	const std::filesystem::path configPath = FindConfigFile();
	Expect(!configPath.empty(), "default config is discoverable from test working directory");
	Expect(configPath.filename() == "runtime-host.json", "default config discovery returns runtime-host.json");
}
}

int main()
{
	TestLoadsRuntimeHostConfig();
	TestCommandLineOverrides();
	TestPreviewDefaultsAreOptIn();
	TestHelpers();

	if (gFailures != 0)
	{
		std::cerr << gFailures << " RenderCadenceCompositorAppConfigProvider test failure(s).\n";
		return 1;
	}

	std::cout << "RenderCadenceCompositorAppConfigProvider tests passed.\n";
	return 0;
}
