#pragma once

#include <mutex>
#include <cstddef>
#include <cstdint>
#include <string>

// HealthTelemetry owns the current operational status snapshot directly, so
// callers can report health without sharing runtime-store state.
class HealthTelemetry
{
public:
	struct SignalStatusSnapshot
	{
		bool hasSignal = false;
		unsigned width = 0;
		unsigned height = 0;
		std::string modeName;
	};

	struct VideoIOStatusSnapshot
	{
		std::string backendName = "decklink";
		std::string modelName;
		bool supportsInternalKeying = false;
		bool supportsExternalKeying = false;
		bool keyerInterfaceAvailable = false;
		bool externalKeyingRequested = false;
		bool externalKeyingActive = false;
		std::string statusMessage;
	};

	struct PerformanceSnapshot
	{
		double frameBudgetMilliseconds = 0.0;
		double renderMilliseconds = 0.0;
		double smoothedRenderMilliseconds = 0.0;
		double completionIntervalMilliseconds = 0.0;
		double smoothedCompletionIntervalMilliseconds = 0.0;
		double maxCompletionIntervalMilliseconds = 0.0;
		uint64_t lateFrameCount = 0;
		uint64_t droppedFrameCount = 0;
		uint64_t flushedFrameCount = 0;
	};

	struct RuntimeEventQueueSnapshot
	{
		std::string queueName = "runtime-events";
		std::size_t depth = 0;
		std::size_t capacity = 0;
		uint64_t droppedCount = 0;
		double oldestEventAgeMilliseconds = 0.0;
	};

	struct RuntimeEventDispatchSnapshot
	{
		uint64_t dispatchCallCount = 0;
		uint64_t dispatchedEventCount = 0;
		uint64_t handlerInvocationCount = 0;
		uint64_t handlerFailureCount = 0;
		double lastDispatchDurationMilliseconds = 0.0;
		double maxDispatchDurationMilliseconds = 0.0;
	};

	struct RuntimeEventMetricsSnapshot
	{
		RuntimeEventQueueSnapshot queue;
		RuntimeEventDispatchSnapshot dispatch;
	};

	struct PersistenceSnapshot
	{
		uint64_t writeSuccessCount = 0;
		uint64_t writeFailureCount = 0;
		bool lastWriteSucceeded = true;
		bool unsavedChanges = false;
		bool newerRequestPending = false;
		std::string lastTargetKind;
		std::string lastTargetPath;
		std::string lastReason;
		std::string lastErrorMessage;
	};

	struct BackendPlayoutSnapshot
	{
		std::string lifecycleState = "NotStarted";
		std::string completionResult = "Unknown";
		std::size_t readyQueueDepth = 0;
		std::size_t readyQueueCapacity = 0;
		std::size_t minReadyQueueDepth = 0;
		std::size_t maxReadyQueueDepth = 0;
		uint64_t readyQueueZeroDepthCount = 0;
		uint64_t readyQueuePushedCount = 0;
		uint64_t readyQueuePoppedCount = 0;
		uint64_t readyQueueDroppedCount = 0;
		uint64_t readyQueueUnderrunCount = 0;
		std::size_t systemFramePoolFree = 0;
		std::size_t systemFramePoolReady = 0;
		std::size_t systemFramePoolScheduled = 0;
		uint64_t systemFrameUnderrunCount = 0;
		uint64_t systemFrameRepeatCount = 0;
		uint64_t systemFrameDropCount = 0;
		double systemFrameAgeAtScheduleMilliseconds = 0.0;
		double systemFrameAgeAtCompletionMilliseconds = 0.0;
		double outputRenderMilliseconds = 0.0;
		double smoothedOutputRenderMilliseconds = 0.0;
		double maxOutputRenderMilliseconds = 0.0;
		double outputFrameAcquireMilliseconds = 0.0;
		double outputFrameRenderRequestMilliseconds = 0.0;
		double outputFrameEndAccessMilliseconds = 0.0;
		double outputRenderQueueWaitMilliseconds = 0.0;
		double outputRenderDrawMilliseconds = 0.0;
		double outputReadbackFenceWaitMilliseconds = 0.0;
		double outputReadbackMapMilliseconds = 0.0;
		double outputReadbackCopyMilliseconds = 0.0;
		double outputCachedCopyMilliseconds = 0.0;
		double outputAsyncQueueMilliseconds = 0.0;
		double outputAsyncQueueBufferMilliseconds = 0.0;
		double outputAsyncQueueSetupMilliseconds = 0.0;
		double outputAsyncQueueReadPixelsMilliseconds = 0.0;
		double outputAsyncQueueFenceMilliseconds = 0.0;
		double outputSyncReadMilliseconds = 0.0;
		uint64_t outputAsyncReadbackMissCount = 0;
		uint64_t outputCachedFallbackCount = 0;
		uint64_t outputSyncFallbackCount = 0;
		uint64_t completedFrameIndex = 0;
		uint64_t scheduledFrameIndex = 0;
		uint64_t scheduledLeadFrames = 0;
		bool actualDeckLinkBufferedFramesAvailable = false;
		uint64_t actualDeckLinkBufferedFrames = 0;
		std::size_t targetDeckLinkBufferedFrames = 0;
		double deckLinkScheduleCallMilliseconds = 0.0;
		uint64_t deckLinkScheduleFailureCount = 0;
		uint64_t measuredLagFrames = 0;
		uint64_t catchUpFrames = 0;
		uint64_t lateStreak = 0;
		uint64_t dropStreak = 0;
		uint64_t lateFrameCount = 0;
		uint64_t droppedFrameCount = 0;
		uint64_t flushedFrameCount = 0;
		bool degraded = false;
		std::string statusMessage;
	};

	struct Snapshot
	{
		SignalStatusSnapshot signal;
		VideoIOStatusSnapshot videoIO;
		PerformanceSnapshot performance;
		RuntimeEventMetricsSnapshot runtimeEvents;
		PersistenceSnapshot persistence;
		BackendPlayoutSnapshot backendPlayout;
	};

	HealthTelemetry() = default;

	void ReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
	bool TryReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);

	void ReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
		bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
		bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
	bool TryReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
		bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
		bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);

	void RecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
	bool TryRecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);

	void RecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
		double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
	bool TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
		double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);

	void RecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity,
		uint64_t droppedCount, double oldestEventAgeMilliseconds);
	bool TryRecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity,
		uint64_t droppedCount, double oldestEventAgeMilliseconds);

	void RecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations,
		std::size_t handlerFailures, double dispatchDurationMilliseconds);
	bool TryRecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations,
		std::size_t handlerFailures, double dispatchDurationMilliseconds);

	void RecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
		const std::string& reason, const std::string& errorMessage, bool newerRequestPending);
	bool TryRecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
		const std::string& reason, const std::string& errorMessage, bool newerRequestPending);

	void RecordBackendPlayoutHealth(const std::string& lifecycleState, const std::string& completionResult,
		std::size_t readyQueueDepth, std::size_t readyQueueCapacity, uint64_t readyQueuePushedCount,
		std::size_t minReadyQueueDepth, std::size_t maxReadyQueueDepth, uint64_t readyQueueZeroDepthCount,
		uint64_t readyQueuePoppedCount, uint64_t readyQueueDroppedCount, uint64_t readyQueueUnderrunCount,
		double outputRenderMilliseconds, double smoothedOutputRenderMilliseconds, double maxOutputRenderMilliseconds,
		double outputFrameAcquireMilliseconds, double outputFrameRenderRequestMilliseconds, double outputFrameEndAccessMilliseconds,
		uint64_t completedFrameIndex, uint64_t scheduledFrameIndex, uint64_t scheduledLeadFrames,
		uint64_t measuredLagFrames, uint64_t catchUpFrames, uint64_t lateStreak, uint64_t dropStreak,
		uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount,
		bool degraded, const std::string& statusMessage);
	bool TryRecordBackendPlayoutHealth(const std::string& lifecycleState, const std::string& completionResult,
		std::size_t readyQueueDepth, std::size_t readyQueueCapacity, uint64_t readyQueuePushedCount,
		std::size_t minReadyQueueDepth, std::size_t maxReadyQueueDepth, uint64_t readyQueueZeroDepthCount,
		uint64_t readyQueuePoppedCount, uint64_t readyQueueDroppedCount, uint64_t readyQueueUnderrunCount,
		double outputRenderMilliseconds, double smoothedOutputRenderMilliseconds, double maxOutputRenderMilliseconds,
		double outputFrameAcquireMilliseconds, double outputFrameRenderRequestMilliseconds, double outputFrameEndAccessMilliseconds,
		uint64_t completedFrameIndex, uint64_t scheduledFrameIndex, uint64_t scheduledLeadFrames,
		uint64_t measuredLagFrames, uint64_t catchUpFrames, uint64_t lateStreak, uint64_t dropStreak,
		uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount,
		bool degraded, const std::string& statusMessage);

	void RecordOutputRenderQueueWait(double queueWaitMilliseconds);
	bool TryRecordOutputRenderQueueWait(double queueWaitMilliseconds);

	void RecordSystemMemoryPlayoutStats(std::size_t freeFrameCount, std::size_t readyFrameCount,
		std::size_t scheduledFrameCount, uint64_t underrunCount, uint64_t repeatCount, uint64_t dropCount,
		double frameAgeAtScheduleMilliseconds, double frameAgeAtCompletionMilliseconds);
	bool TryRecordSystemMemoryPlayoutStats(std::size_t freeFrameCount, std::size_t readyFrameCount,
		std::size_t scheduledFrameCount, uint64_t underrunCount, uint64_t repeatCount, uint64_t dropCount,
		double frameAgeAtScheduleMilliseconds, double frameAgeAtCompletionMilliseconds);

	void RecordDeckLinkBufferTelemetry(bool actualBufferedFramesAvailable, uint64_t actualBufferedFrames,
		std::size_t targetBufferedFrames, double scheduleCallMilliseconds, uint64_t scheduleFailureCount);
	bool TryRecordDeckLinkBufferTelemetry(bool actualBufferedFramesAvailable, uint64_t actualBufferedFrames,
		std::size_t targetBufferedFrames, double scheduleCallMilliseconds, uint64_t scheduleFailureCount);

	void RecordOutputRenderPipelineTiming(
		double drawMilliseconds,
		double fenceWaitMilliseconds,
		double mapMilliseconds,
		double readbackCopyMilliseconds,
		double cachedCopyMilliseconds,
		double asyncQueueMilliseconds,
		double asyncQueueBufferMilliseconds,
		double asyncQueueSetupMilliseconds,
		double asyncQueueReadPixelsMilliseconds,
		double asyncQueueFenceMilliseconds,
		double syncReadMilliseconds,
		bool asyncReadbackMissed,
		bool cachedFallbackUsed,
		bool syncFallbackUsed);
	bool TryRecordOutputRenderPipelineTiming(
		double drawMilliseconds,
		double fenceWaitMilliseconds,
		double mapMilliseconds,
		double readbackCopyMilliseconds,
		double cachedCopyMilliseconds,
		double asyncQueueMilliseconds,
		double asyncQueueBufferMilliseconds,
		double asyncQueueSetupMilliseconds,
		double asyncQueueReadPixelsMilliseconds,
		double asyncQueueFenceMilliseconds,
		double syncReadMilliseconds,
		bool asyncReadbackMissed,
		bool cachedFallbackUsed,
		bool syncFallbackUsed);

	SignalStatusSnapshot GetSignalStatusSnapshot() const;
	VideoIOStatusSnapshot GetVideoIOStatusSnapshot() const;
	PerformanceSnapshot GetPerformanceSnapshot() const;
	RuntimeEventMetricsSnapshot GetRuntimeEventMetricsSnapshot() const;
	PersistenceSnapshot GetPersistenceSnapshot() const;
	BackendPlayoutSnapshot GetBackendPlayoutSnapshot() const;
	Snapshot GetSnapshot() const;

private:
	mutable std::mutex mMutex;
	SignalStatusSnapshot mSignalStatus;
	VideoIOStatusSnapshot mVideoIOStatus;
	PerformanceSnapshot mPerformance;
	RuntimeEventMetricsSnapshot mRuntimeEvents;
	PersistenceSnapshot mPersistence;
	BackendPlayoutSnapshot mBackendPlayout;
};
