#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
