#include "BookList.hpp"

#include <algorithm>
#include <glibmm/main.h>
#include <sigc++/signal.h>

BookList::BookList(DatabaseManager& database)
	: m_database(database) {}

// -------------------------------------------------------------------
// Load
// -------------------------------------------------------------------
void BookList::loadAll() {
	std::lock_guard<std::mutex> lock(m_mutex);
	m_books = m_database.load_all_books();
	m_signalReset.emit();
	m_signalBulkChanged.emit();
}

// -------------------------------------------------------------------
// CRUD
// -------------------------------------------------------------------
bool BookList::upsert(const Book& book) {
	if (!m_database.upsert_book(book))
		return false;

	std::lock_guard<std::mutex> lock(m_mutex);
	int idx = indexOfUnlocked(book.id());
	if (idx < 0) {
		m_books.push_back(book);
		m_signalBookAdded.emit(book);
	} else {
		m_books[static_cast<std::size_t>(idx)] = book;
		m_signalBookUpdated.emit(book);
	}
	return true;
}

bool BookList::removeById(const std::string& id) {
	if (!m_database.remove_book(id))
		return false;

	std::lock_guard<std::mutex> lock(m_mutex);
	int idx = indexOfUnlocked(id);
	if (idx >= 0) {
		const std::string removedId = m_books[static_cast<std::size_t>(idx)].id();
		m_books.erase(m_books.begin() + idx);
		m_signalBookRemoved.emit(removedId);
		return true;
	}
	return false;
}

std::optional<Book> BookList::findById(const std::string& id) const {
	std::lock_guard<std::mutex> lock(m_mutex);
	int idx = indexOfUnlocked(id);
	if (idx < 0) return std::nullopt;
	return m_books[static_cast<std::size_t>(idx)];
}

int BookList::indexOf(const std::string& id) const {
	std::lock_guard<std::mutex> lock(m_mutex);
	return indexOfUnlocked(id);
}

int BookList::indexOfUnlocked(const std::string& id) const {
	auto it = std::find_if(m_books.begin(), m_books.end(),
						   [&](const Book& b) { return b.id() == id; });
	if (it == m_books.end()) return -1;
	return static_cast<int>(std::distance(m_books.begin(), it));
}

// -------------------------------------------------------------------
// Bulk
// -------------------------------------------------------------------
bool BookList::upsertMany(const std::vector<Book>& items) {
	bool any = false;
	for (const auto& b : items)
		any = m_database.upsert_book(b) || any;

	if (!any) return false;

	{
		std::lock_guard<std::mutex> lock(m_mutex);
		for (const auto& b : items) {
			int idx = indexOfUnlocked(b.id());
			if (idx < 0)
				m_books.push_back(b);
			else
				m_books[static_cast<std::size_t>(idx)] = b;
		}
	}

	m_signalBulkChanged.emit();
	return true;
}

// -------------------------------------------------------------------
// Enqueue versions (safe from worker threads)
// -------------------------------------------------------------------
void BookList::upsertEnqueue(Book book) {
	Glib::signal_idle().connect_once([this, book = std::move(book)]() mutable {
		upsert(book);
	});
}

void BookList::removeByIdEnqueue(std::string id) {
	Glib::signal_idle().connect_once([this, id = std::move(id)]() mutable {
		removeById(id);
	});
}

void BookList::loadAllEnqueue() {
	Glib::signal_idle().connect_once([this]() {
		loadAll();
	});
}

