#include "torlinkc/ui/sort.hpp"

#include <algorithm>

namespace torlinkc::ui {

namespace {

bool sameSort(const Sort& a, const Sort& b) {
  if (!a || !b) return !a && !b;
  return a->field == b->field && a->dir == b->dir;
}

std::string fieldName(SortField f) {
  switch (f) {
    case SortField::Size:
      return "size";
    case SortField::Seeders:
      return "seeders";
    case SortField::Source:
      return "source";
    case SortField::Added:
      return "added";
  }
  return "size";
}

}  // namespace

const std::vector<Sort>& sortCycle() {
  static const std::vector<Sort> cycle = {
      std::nullopt,
      SortState{SortField::Size, SortDir::Asc},
      SortState{SortField::Size, SortDir::Desc},
      SortState{SortField::Seeders, SortDir::Asc},
      SortState{SortField::Seeders, SortDir::Desc},
      SortState{SortField::Source, SortDir::Asc},
      SortState{SortField::Source, SortDir::Desc},
      SortState{SortField::Added, SortDir::Asc},
      SortState{SortField::Added, SortDir::Desc},
  };
  return cycle;
}

Sort nextSort(const Sort& current) {
  const auto& cycle = sortCycle();
  auto it = std::find_if(cycle.begin(), cycle.end(), [&](const Sort& s) { return sameSort(s, current); });
  const std::size_t i = it == cycle.end() ? 0 : static_cast<std::size_t>(it - cycle.begin());
  return cycle[(i + 1) % cycle.size()];
}

std::string sortArrow(SortDir dir) { return dir == SortDir::Asc ? "▴" : "▾"; }

std::string sortLabel(const Sort& sort) {
  if (!sort) return "default";
  return fieldName(sort->field) + " " + sortArrow(sort->dir);
}

std::vector<TorrentResult> sortResults(const std::vector<TorrentResult>& list, const Sort& sort) {
  std::vector<TorrentResult> arr = list;
  if (!sort) return arr;
  // `a` sorts before `b` exactly when this JS-style combined comparator
  // value (mul * primary, falling through to a tiebreak) is negative --
  // mirrors ui/sort.ts::sortResults's `mul * (...) || (...)` chains, just
  // spelled out instead of relying on JS's truthy-`||` short-circuit.
  const long long mul = sort->dir == SortDir::Asc ? 1 : -1;

  switch (sort->field) {
    case SortField::Size:
      std::stable_sort(arr.begin(), arr.end(), [&](const TorrentResult& a, const TorrentResult& b) {
        const long long primary = mul * (a.sizeBytes - b.sizeBytes);
        return primary != 0 ? primary < 0 : (b.seeders - a.seeders) < 0;
      });
      break;
    case SortField::Seeders:
      std::stable_sort(arr.begin(), arr.end(), [&](const TorrentResult& a, const TorrentResult& b) {
        const long long primary = mul * (a.seeders - b.seeders);
        return primary != 0 ? primary < 0 : (b.added.value_or(0) - a.added.value_or(0)) < 0;
      });
      break;
    case SortField::Source:
      std::stable_sort(arr.begin(), arr.end(), [&](const TorrentResult& a, const TorrentResult& b) {
        const long long primary = mul * a.source.compare(b.source);
        return primary != 0 ? primary < 0 : (b.seeders - a.seeders) < 0;
      });
      break;
    case SortField::Added:
      std::stable_sort(arr.begin(), arr.end(), [&](const TorrentResult& a, const TorrentResult& b) {
        const long long primary = mul * (a.added.value_or(0) - b.added.value_or(0));
        return primary != 0 ? primary < 0 : (b.seeders - a.seeders) < 0;
      });
      break;
  }
  return arr;
}

}  // namespace torlinkc::ui
