#pragma once
#include "fuzzy-scorer.hpp"
#include "glyph/emoji.hpp"
#include "glyph/glyph.hpp"
#include <QDateTime>
#include <qobject.h>
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "fuzzy/scored.hpp"

class OmniDatabase;

/**
 * Provides metadata services for glyphs (emoji and symbols alike): visit counts, pins, skin-tone
 * overrides and custom keywords, persisted to a simple JSON file under the data directory.
 * The glyph table itself is statically generated by gen.ts and stored inside `glyph.cpp`.
 */

struct GlyphItemHash {
  size_t operator()(const glyph::Item *data) const { return std::hash<std::string_view>()(data->character); }
};

// `emoji` field key kept for backward compat with existing user data.
struct SerializedEmojiMetadata {
  std::string emoji;
  std::uint32_t visitCount = 0;
  std::optional<std::uint64_t> pinnedAt;
  std::optional<std::uint64_t> lastVisitedAt;
  std::optional<std::string> skinTone;
  std::optional<std::string> keyword;
};

struct GlyphMetadata {
  const glyph::Item *data = nullptr;
  uint32_t visitCount = 0;
  std::optional<QDateTime> pinnedAt;
  std::optional<emoji::SkinTone> tone;
  std::string keyword; // concatenated as one, space delimited
};

class GlyphService : public QObject {
  Q_OBJECT

signals:
  void pinned(std::string_view emoji) const;
  void unpinned(std::string_view emoji) const;
  void visited(std::string_view emoji) const;
  void rankingReset(std::string_view emoji) const;
  void skintoneChanged(std::string_view emoji) const;
  void keywordsChanged(std::string_view emoji) const;

public:
  GlyphService(const std::filesystem::path &path, OmniDatabase *legacyDb = nullptr);

  std::span<Scored<const glyph::Item *>> search(std::string_view query) const;

  /**
   * Map metadata to the provided list of items.
   */
  std::vector<GlyphMetadata> mapMetadata(const std::vector<const glyph::Item *> &items);
  GlyphMetadata mapMetadata(std::string_view emoji);

  // Returns all the visited and pinned emojis. pinned emojis are always included first in the list.
  std::vector<GlyphMetadata> getVisited() const;

  bool pin(std::string_view emoji);
  bool unpin(std::string_view emoji);
  bool registerVisit(std::string_view emoji);
  bool resetRanking(std::string_view emoji);
  bool setSkinTone(std::string_view emoji, emoji::SkinTone tone);
  bool resetSkinTone(std::string_view emoji);
  bool setKeywords(std::string_view emoji, const std::string &text);

private:
  void migrateFromDatabase(OmniDatabase &db);

  SerializedEmojiMetadata const *findEntry(std::string_view emoji) const;
  SerializedEmojiMetadata *findEntry(std::string_view emoji);

  // Return metadata entry for emoji, or create it if it doesn't exist
  SerializedEmojiMetadata &entryFor(std::string_view emoji);

  bool load();
  bool save();

  std::filesystem::path m_path;
  std::vector<SerializedEmojiMetadata> m_entries;
  std::string m_buf;

  FuzzyScorer<glyph::Item> m_scorer;
};
