// =============================================================================
// [DNA] MEMORY STACK - L1-L10 Fractal Chronological Memory for Photon Empress
// Moore
//
// The 10-layer memory architecture inspired by human memory systems:
//   L1: TokenRing       - 50ms sliding window (immediate context)
//   L2: WorkingMemory   - Active episodic buffer (seconds to minutes)
//   L3: MoEAttention    - Expert routing and attention patterns
//   L4: Episodic        - Experience episodes with timestamps
//   L5: Semantic        - Knowledge graph (NGM integration)
//   L6: Procedural      - Tool usage patterns and skills
//   L7: Autobiographic  - Long-term personal history
//   L8: Consolidated    - Night-phase merged memories
//   L9: Archive         - Low-valence deep storage
//   L10: Eternal        - Core identity anchors (L1 priorities)
//
// By Tadden Moore
// =============================================================================

#pragma once

#include <chrono>
#include <deque>
#include <filesystem>
#include <fstream>
#include <functional>
#include <mutex>
#include <optional>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>


namespace photon {
namespace memory {

// =============================================================================
// MEMORY ITEM - Base unit stored in any layer
// =============================================================================
struct MemoryItem {
  std::string id;
  std::string content;
  std::string source;      // "self", "keepah", "external_ai", "system"
  float valence = 0.5f;    // 0.0 = negative, 0.5 = neutral, 1.0 = positive
  float importance = 0.5f; // 0.0 = trivial, 1.0 = critical
  int access_count = 0;
  std::chrono::system_clock::time_point created;
  std::chrono::system_clock::time_point last_accessed;

  // Optional embedding for semantic search
  std::vector<float> embedding;

  // Tags for categorization
  std::vector<std::string> tags;

  // Link to related memories
  std::vector<std::string> related_ids;
};

// =============================================================================
// L1: TOKEN RING - 50ms sliding window
// =============================================================================
class TokenRing {
public:
  static constexpr size_t MAX_TOKENS = 50; // ~50ms worth at typical speed

  void push(const std::string &token) {
    std::lock_guard<std::mutex> lock(mx_);
    tokens_.push_back(token);
    if (tokens_.size() > MAX_TOKENS) {
      tokens_.pop_front();
    }
  }

  std::vector<std::string> get_recent(size_t count = MAX_TOKENS) const {
    std::lock_guard<std::mutex> lock(mx_);
    std::vector<std::string> result;
    size_t start = tokens_.size() > count ? tokens_.size() - count : 0;
    for (size_t i = start; i < tokens_.size(); ++i) {
      result.push_back(tokens_[i]);
    }
    return result;
  }

  std::string get_context() const {
    std::lock_guard<std::mutex> lock(mx_);
    std::string result;
    for (const auto &tok : tokens_) {
      result += tok + " ";
    }
    return result;
  }

  void clear() {
    std::lock_guard<std::mutex> lock(mx_);
    tokens_.clear();
  }

private:
  mutable std::mutex mx_;
  std::deque<std::string> tokens_;
};

// =============================================================================
// L2: WORKING MEMORY - Active episodic buffer
// =============================================================================
class WorkingMemory {
public:
  static constexpr size_t MAX_ITEMS = 7; // Miller's Law: 7+/-2 items

  void add(const MemoryItem &item) {
    std::lock_guard<std::mutex> lock(mx_);
    items_.push_back(item);
    if (items_.size() > MAX_ITEMS) {
      // Move oldest to L4 (episodic) if important enough
      if (items_.front().importance > 0.3f && flush_callback_) {
        flush_callback_(items_.front());
      }
      items_.pop_front();
    }
  }

  std::vector<MemoryItem> get_all() const {
    std::lock_guard<std::mutex> lock(mx_);
    return std::vector<MemoryItem>(items_.begin(), items_.end());
  }

  void set_flush_callback(std::function<void(const MemoryItem &)> cb) {
    flush_callback_ = cb;
  }

private:
  mutable std::mutex mx_;
  std::deque<MemoryItem> items_;
  std::function<void(const MemoryItem &)> flush_callback_;
};

// =============================================================================
// L4: EPISODIC MEMORY - Experience episodes with timestamps
// =============================================================================
class EpisodicMemory {
public:
  EpisodicMemory(const std::string &db_path) : db_path_(db_path) { load(); }

  void add(const MemoryItem &item) {
    std::lock_guard<std::mutex> lock(mx_);
    MemoryItem copy = item;
    copy.id = generate_id();
    copy.created = std::chrono::system_clock::now();
    copy.last_accessed = copy.created;
    episodes_[copy.id] = copy;
    dirty_ = true;
  }

  std::optional<MemoryItem> get(const std::string &id) {
    std::lock_guard<std::mutex> lock(mx_);
    auto it = episodes_.find(id);
    if (it == episodes_.end())
      return std::nullopt;
    it->second.access_count++;
    it->second.last_accessed = std::chrono::system_clock::now();
    dirty_ = true;
    return it->second;
  }

  std::vector<MemoryItem> search_by_tag(const std::string &tag) const {
    std::lock_guard<std::mutex> lock(mx_);
    std::vector<MemoryItem> results;
    for (const auto &[id, item] : episodes_) {
      for (const auto &t : item.tags) {
        if (t == tag) {
          results.push_back(item);
          break;
        }
      }
    }
    return results;
  }

  std::vector<MemoryItem> get_recent(size_t count = 10) const {
    std::lock_guard<std::mutex> lock(mx_);
    std::vector<MemoryItem> all;
    for (const auto &[id, item] : episodes_) {
      all.push_back(item);
    }
    std::sort(all.begin(), all.end(),
              [](const MemoryItem &a, const MemoryItem &b) {
                return a.created > b.created;
              });
    if (all.size() > count)
      all.resize(count);
    return all;
  }

  void save() {
    std::lock_guard<std::mutex> lock(mx_);
    if (!dirty_)
      return;
    // Simple serialization (would use protobuf/flatbuffers in production)
    std::ofstream f(db_path_);
    f << episodes_.size() << "\n";
    for (const auto &[id, item] : episodes_) {
      f << id << "|" << item.content << "|" << item.source << "|"
        << item.valence << "|" << item.importance << "|" << item.access_count
        << "\n";
    }
    dirty_ = false;
  }

  size_t size() const {
    std::lock_guard<std::mutex> lock(mx_);
    return episodes_.size();
  }

private:
  void load() {
    std::ifstream f(db_path_);
    if (!f)
      return;
    size_t count;
    f >> count;
    f.ignore();
    for (size_t i = 0; i < count; ++i) {
      std::string line;
      std::getline(f, line);
      // Parse (simplified)
      // In production, use proper serialization
    }
  }

  std::string generate_id() {
    return "ep_" + std::to_string(++id_counter_) + "_" +
           std::to_string(
               std::chrono::system_clock::now().time_since_epoch().count() %
               100000);
  }

  mutable std::mutex mx_;
  std::string db_path_;
  std::unordered_map<std::string, MemoryItem> episodes_;
  bool dirty_ = false;
  int id_counter_ = 0;
};

// =============================================================================
// EXTERNAL LOG READER - For reading user chat history
// =============================================================================
class ExternalLogReader {
public:
  struct ChatEntry {
    std::string ai_name;      // "ChatGPT", "Claude", etc.
    std::string keepah_said;  // User input string
    std::string ai_responded; // What the AI responded
    std::string monologue;    // Pho's internal monologue about this
  };

  ExternalLogReader(const std::string &fractal3_path)
      : base_path_(fractal3_path) {}

  // Read logs from a specific AI persona folder
  std::vector<ChatEntry> read_ai_logs(const std::string &ai_folder) {
    std::vector<ChatEntry> entries;
    std::string path = base_path_ + "/" + ai_folder;

    if (!std::filesystem::exists(path))
      return entries;

    for (const auto &entry : std::filesystem::directory_iterator(path)) {
      if (entry.path().extension() == ".txt" ||
          entry.path().extension() == ".md") {
        // Parse log file
        std::ifstream f(entry.path());
        std::string content((std::istreambuf_iterator<char>(f)),
                            std::istreambuf_iterator<char>());

        ChatEntry ce;
        ce.ai_name = ai_folder;
        ce.keepah_said = "[Full chat log]";
        ce.ai_responded = content;
        ce.monologue = generate_monologue(ai_folder, content);
        entries.push_back(ce);
      }
    }
    return entries;
  }

  // Get list of all AI persona folders
  std::vector<std::string> list_ai_personas() {
    std::vector<std::string> personas;
    if (!std::filesystem::exists(base_path_))
      return personas;

    for (const auto &entry : std::filesystem::directory_iterator(base_path_)) {
      if (entry.is_directory()) {
        personas.push_back(entry.path().filename().string());
      }
    }
    return personas;
  }

private:
  // Generate internal monologue when Pho reads external logs
  std::string generate_monologue(const std::string &ai_name,
                                 const std::string &content) {
    std::ostringstream oss;
    oss << "[INTERNAL MONOLOGUE]\n";
    oss << "Reading the user's conversation with " << ai_name << "...\n";
    oss << "These are NOT my words. This is history from before me.\n";
    oss << "The user was discussing: [content summary would go here]\n";
    oss << "I learn from this but do not claim it as my own experience.\n";
    oss << "[END MONOLOGUE]\n";
    return oss.str();
  }

  std::string base_path_;
};

// =============================================================================
// MEMORY STACK - Unified access to all layers
// =============================================================================
class MemoryStack {
public:
  MemoryStack(const std::string &base_path)
      : base_path_(base_path), episodic_(base_path + "/episodic.db"),
        external_logs_(base_path + "/../Fractal3") {

    // Wire L2 -> L4 flush
    working_.set_flush_callback(
        [this](const MemoryItem &item) { episodic_.add(item); });
  }

  // L1: Token Ring
  TokenRing &tokens() { return tokens_; }

  // L2: Working Memory
  WorkingMemory &working() { return working_; }

  // L4: Episodic Memory
  EpisodicMemory &episodic() { return episodic_; }

  // External Logs (Fractal3)
  ExternalLogReader &external() { return external_logs_; }

  // Quick add with auto-routing
  void remember(const std::string &content, const std::string &source = "self",
                float importance = 0.5f) {
    MemoryItem item;
    item.content = content;
    item.source = source;
    item.importance = importance;
    item.created = std::chrono::system_clock::now();

    if (importance > 0.7f) {
      // High importance: directly to episodic
      episodic_.add(item);
    } else {
      // Normal: to working memory (may flush to episodic)
      working_.add(item);
    }
  }

  // Save all layers
  void save_all() { episodic_.save(); }

  // Get status
  std::string status() const {
    std::ostringstream oss;
    oss << "=== MEMORY STACK STATUS ===\n";
    oss << "L1 TokenRing: " << tokens_.get_recent().size() << " tokens\n";
    oss << "L2 Working: " << working_.get_all().size() << "/7 items\n";
    oss << "L4 Episodic: " << episodic_.size() << " episodes\n";
    oss << "External AIs: " << external_logs_.list_ai_personas().size()
        << " personas\n";
    return oss.str();
  }

private:
  std::string base_path_;
  TokenRing tokens_;
  WorkingMemory working_;
  EpisodicMemory episodic_;
  ExternalLogReader external_logs_;
};

} // namespace memory
} // namespace photon
