#pragma once

#include "PersistenceRequest.h"

#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <deque>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>

struct PersistenceWriterMetrics
{
	std::size_t pendingCount = 0;
	uint64_t enqueuedCount = 0;
	uint64_t coalescedCount = 0;
	uint64_t writtenCount = 0;
	uint64_t failedCount = 0;
};

struct PersistenceWriteResult
{
	PersistenceTargetKind targetKind = PersistenceTargetKind::RuntimeState;
	std::string targetPath;
	std::string reason;
	bool succeeded = false;
	std::string errorMessage;
	bool newerRequestPending = false;
};

class PersistenceWriter
{
public:
	using SnapshotSink = std::function<bool(const PersistenceSnapshot&, std::string&)>;
	using ResultCallback = std::function<void(const PersistenceWriteResult&)>;

	explicit PersistenceWriter(
		std::chrono::milliseconds debounceDelay = std::chrono::milliseconds(50),
		SnapshotSink sink = SnapshotSink());
	~PersistenceWriter();

	void SetResultCallback(ResultCallback callback);
	bool WriteSnapshot(const PersistenceSnapshot& snapshot, std::string& error);
	bool EnqueueSnapshot(const PersistenceSnapshot& snapshot, std::string& error);
	bool StopAndFlush(std::chrono::milliseconds timeout, std::string& error);
	PersistenceWriterMetrics GetMetrics() const;

private:
	struct PendingSnapshot
	{
		PersistenceSnapshot snapshot;
		std::chrono::steady_clock::time_point readyAt;
	};

	bool ValidateSnapshot(const PersistenceSnapshot& snapshot, std::string& error) const;
	bool WriteSnapshotThroughSink(const PersistenceSnapshot& snapshot, std::string& error) const;
	void PublishWriteResult(const PersistenceSnapshot& snapshot, bool succeeded, const std::string& errorMessage, bool newerRequestPending);
	void StartWorkerLocked();
	void WorkerMain();
	std::size_t PendingCountLocked() const;

	std::chrono::milliseconds mDebounceDelay;
	SnapshotSink mSink;
	ResultCallback mResultCallback;
	mutable std::mutex mMutex;
	std::condition_variable mCondition;
	std::thread mWorker;
	bool mWorkerRunning = false;
	bool mStopping = false;
	bool mAcceptingRequests = true;
	std::unordered_map<std::string, PendingSnapshot> mDebouncedSnapshots;
	std::deque<PersistenceSnapshot> mImmediateSnapshots;
	uint64_t mEnqueuedCount = 0;
	uint64_t mCoalescedCount = 0;
	uint64_t mWrittenCount = 0;
	uint64_t mFailedCount = 0;
};
