#include "RuntimeStateJson.h"

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

namespace
{
int gFailures = 0;

void ExpectContains(const std::string& text, const std::string& fragment, const std::string& message)
{
	if (text.find(fragment) != std::string::npos)
		return;

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

std::filesystem::path MakeTestRoot()
{
	const auto stamp = std::chrono::steady_clock::now().time_since_epoch().count();
	const std::filesystem::path root = std::filesystem::temp_directory_path() / ("render-cadence-state-json-tests-" + std::to_string(stamp));
	std::filesystem::create_directories(root);
	return root;
}

void WriteFile(const std::filesystem::path& path, const std::string& contents)
{
	std::filesystem::create_directories(path.parent_path());
	std::ofstream output(path, std::ios::binary);
	output << contents;
}
}

int main()
{
	RenderCadenceCompositor::AppConfig config = RenderCadenceCompositor::DefaultAppConfig();
	config.outputVideoFormat = "1080p";
	config.outputFrameRate = "59.94";

	RenderCadenceCompositor::CadenceTelemetrySnapshot telemetry;
	telemetry.renderFps = 59.94;
	telemetry.renderFrameMilliseconds = 2.5;
	telemetry.renderFrameBudgetUsedPercent = 15.0;
	telemetry.renderFrameMaxMilliseconds = 4.0;
	telemetry.readbackQueueMilliseconds = 0.6;
	telemetry.completedReadbackCopyMilliseconds = 1.2;
	telemetry.completedDrops = 3;
	telemetry.acquireMisses = 4;
	telemetry.shaderBuildsCommitted = 1;

	const std::filesystem::path root = MakeTestRoot();
	WriteFile(root / "solid-color" / "shader.slang", "float4 shadeVideo(float2 uv) { return float4(uv, 0.0, 1.0); }\n");
	WriteFile(root / "solid-color" / "shader.json", R"({
		"id": "solid-color",
		"name": "Solid Color",
		"description": "A single color shader.",
		"category": "Generator",
		"entryPoint": "shadeVideo",
		"parameters": [
			{
				"id": "color",
				"label": "Color",
				"description": "Output color.",
				"type": "color",
				"default": [1.0, 0.25, 0.5, 1.0],
				"min": [0.0, 0.0, 0.0, 0.0],
				"max": [1.0, 1.0, 1.0, 1.0],
				"step": [0.01, 0.01, 0.01, 0.01]
			}
		]
	})");

	RenderCadenceCompositor::SupportedShaderCatalog shaderCatalog;
	std::string error;
	ExpectContains(shaderCatalog.Load(root, 4, error) ? "loaded" : error, "loaded", "test shader catalog should load");

	RenderCadenceCompositor::RuntimeLayerModel layerModel;
	layerModel.InitializeSingleLayer(shaderCatalog, "solid-color", error);
	RuntimeShaderArtifact artifact;
	artifact.shaderId = "solid-color";
	artifact.displayName = "Solid Color";
	artifact.fragmentShaderSource = "void main(){}";
	artifact.message = "Runtime shader committed.";
	layerModel.MarkBuildReady(artifact, error);
	const RenderCadenceCompositor::RuntimeLayerModelSnapshot layerSnapshot = layerModel.Snapshot();

	const std::string json = RenderCadenceCompositor::RuntimeStateToJson(RenderCadenceCompositor::RuntimeStateJsonInput{
		config,
		telemetry,
		8080,
		true,
		"DeckLink scheduled output running.",
		shaderCatalog,
		layerSnapshot
	});

	ExpectContains(json, "\"shaders\":[{\"id\":\"solid-color\"", "state JSON should include supported shaders");
	ExpectContains(json, "\"layerCount\":1", "state JSON should expose the display layer count");
	ExpectContains(json, "\"layers\":[{\"id\":\"runtime-layer-1\"", "state JSON should expose the active display layer");
	ExpectContains(json, "\"parameters\":[{\"id\":\"color\"", "state JSON should expose active shader parameters");
	ExpectContains(json, "\"type\":\"color\"", "state JSON should serialize parameter types for the UI");
	ExpectContains(json, "\"width\":1920", "state JSON should expose output width");
	ExpectContains(json, "\"height\":1080", "state JSON should expose output height");
	ExpectContains(json, "\"renderMs\":2.5", "state JSON should expose top-level render timing");
	ExpectContains(json, "\"budgetUsedPercent\":15", "state JSON should expose top-level render budget percentage");
	ExpectContains(json, "\"renderFrameMs\":2.5", "state JSON should expose cadence render timing");
	ExpectContains(json, "\"readbackQueueMs\":0.6", "state JSON should expose readback queue timing");
	ExpectContains(json, "\"completedReadbackCopyMs\":1.2", "state JSON should expose completed readback copy timing");
	ExpectContains(json, "\"completedDrops\":3", "state JSON should expose completed drop count");
	ExpectContains(json, "\"acquireMisses\":4", "state JSON should expose acquire miss count");

	std::filesystem::remove_all(root);

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

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