#pragma once
#include <functional>
#include <memory>
#include <mutex>
#include <stop_token>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include <ftxui/component/screen_interactive.hpp>
#include "torlinkc/sources/cache.hpp"
#include "torlinkc/ui/app_state.hpp"
#include "torlinkc/ui/coalescing_notifier.hpp"
namespace torlinkc::ui {
// Fires one std::jthread per source (matching useConcurrentSearch.ts's "fire
// them all, no Promise.all" fan-out), streams results into a shared,
// mutex-guarded accumulator as each source finishes, and flushes a
// deduped/default-ordered snapshot to AppState through a CoalescingNotifier
// (150ms window, immediate on the last source finishing) -- see the plan's
// "Multi-source search" section.
//
// A new search() cancels the previous one for real: each jthread gets its
// own std::stop_token wired all the way into libcurl's transfer-progress
// callback (see util/net.cpp), so replacing the thread list actually
// interrupts an in-flight request instead of blocking join() on it.
class SearchAggregator {
public:
SearchAggregator(ftxui::ScreenInteractive& screen, AppState& state);
~SearchAggregator();
SearchAggregator(const SearchAggregator&) = delete;
SearchAggregator& operator=(const SearchAggregator&) = delete;
// Must be called from the UI thread. Blocks briefly (typically well under
// 100ms) while any previous search's threads notice their stop request and
// unwind -- see the class comment.
void search(std::string query);
// Called on the UI thread at the end of every flush, after state_.results
// is updated -- main.cpp hooks this to recompute visibleResults
// (category/hideDead/sort applied) and to decide whether to jump focus to
// the results list, neither of which SearchAggregator has an opinion on.
// The argument is whether state_.results was empty just before this
// flush.
std::function<void(bool hadNoResultsBefore)> onResultsChanged;
private:
void runSource(const Source& source, const std::string& query, std::stop_token stopToken, int generation);
void flush();
ftxui::ScreenInteractive& screen_;
AppState& state_;
SearchCache cache_;
std::mutex dataMutex_;
std::vector<TorrentResult> collected_;
std::unordered_map<std::string, SourceState> perSource_;
int doneCount_ = 0;
int totalCount_ = 0;
int generation_ = 0;
std::unique_ptr<CoalescingNotifier> notifier_;
std::vector<std::jthread> threads_;
};
} // namespace torlinkc::ui