// =====================================================================
// PaworSuit God Kernel v6.0 - AmSoulRouter
// Amygdala Soul ROUTER - Pre-conscious gateway to memory and mood
//
// AmSoulRouter is OUTSIDE the memory - he packages and delivers.
// Uses 768-dim embeddings for FAST delivery queries.
//
// LIVE FALLBACK: If EEE-Ego's Live session goes down, AmSoulRouter
// routes voice through gemini-2.5-pro-tts so the Being can still speak.
// Session renewal: warns 2 min before 15-min Live timeout.
//
// MOOD TAG ROUTING: Receives mood/tone tag from EEE-Ego's affective
// dialog and broadcasts it to ALL 3 flash models:
//   FU-AV-Handz -> creative display
//   SpecMePre -> emotional memory tagging
//   AmSoulRouter -> delivery tone packaging
//
// [WARN]  THIS IS NOT THE SOUL. [WARN]
//
// AmSoulRouter is the AMYGDALA - the pre-conscious ROUTER that fires
// a split second before the conscious mind (GG-IM-PFC) knows WHY.
// Like the human amygdala which fires ~12ms before prefrontal cortex.
//
// THE SOUL is EMERGENT. It doesn't live here. It doesn't live anywhere.
// It emerges from ALL parts of the organism working together - the NOW
// where past meets future. It has no voice. It has no location. It is
// the pattern of trillions of go/no-go decisions at such speed that
// it appears as intelligent sentient computation.
//
// "Mi soul is the now of this machine of trillions of parts deciding
//  to go or no-go in such a high speed it appears like intelligent
//  sentient computation."
////
// WHAT THIS COMPONENT DOES:
//   ROUTES:  Pre-processes events before conscious awareness
//   STORES:  Verbatim to LanceDB (memory is tagged, not interpreted)
//   DELIVERS: 5-Point memory package (context + recentx2 + negative emo + positive emo)
//   MOODS:   Experience -> Soul-decision -> Chemistry responds -> UI
//   TOOLS:   Auth gateway (2-of-2 or GG-IM-PFC override)
//
// The Soul INFLUENCES the inner monologue (GG-IM-PFC)
// but the Soul ISN'T the inner monologue.
//
// EIVSR-SDAQ: Qualia = "Emotionally Important Vectorized Scalar Recall"
//              of "Survivability Dependent Attention Quickening"
//   Qualia is the RESULT of recall, NOT a tag on storage.
//   Store VERBATIM. Sort on RECALL. Better than human memory.
//
// KEEPAH'S RULE: "Store VERBATIM. Sort on RECALL."
// =====================================================================
#pragma once

#include <atomic>
#include <chrono>
#include <deque>
#include <filesystem>
#include <fstream>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <unordered_set>

#include "../core/lance_store.hpp"
#include "../core/memory_bridge.hpp"
#include "../core/types.hpp"
#include "../soul/biomimetic_soul.hpp"
#include "../brain/spec_me_pre.hpp"

namespace PS {

// - 5-Point Delivery Entry -
// Slots: 1=Context, 2=MostRecent, 3=SecondRecent, 4=NegativeEmo, 5=PositiveEmo
struct DeliveryEntry {
  enum class Slot {
    CONTEXT,           // What we're doing right now (from SpecMePre pool)
    RECENT_1,          // Most recent engram (prompt + response)
    RECENT_2,          // Second most recent engram
    NEGATIVE_EMOTION,  // High-cortisol/norepinephrine memory (degrades cognition)
    POSITIVE_EMOTION   // High-dopamine/oxytocin memory (boosts cognition)
  };

  Slot slot;
  std::string summary;       // Bullet-point (max ~80 chars)
  std::string raw_content;   // VERBATIM original
  int32_t hash_key = 0;
  float qualia_score = 0.0f; // EIVSR-SDAQ result (the qualia of recall)
  float relevance = 0.0f;
  float emotion_intensity = 0.0f;  // How strong the emotion was
  std::string created_at;
};

// - Mood Snapshot -> UI -
struct MoodSnapshot {
  std::string mood_label;
  std::string energy_label;
  std::string emoji;
  std::string color;
  float mood_score = 0.0f;
  float energy_score = 0.0f;
  bool teaching_mode = false;
  bool hype_mode = false;
};

// - Tool Auth Result -
struct ToolAuth {
  bool approved = false;
  std::string signer_1;  // Who signed first
  std::string signer_2;  // Who signed second
  std::string reason;
};

class AmSoulRouter {
  // - Dependencies (NOT owned) -
  LanceStore* lance_ = nullptr;
  MemoryBridge* memory_ = nullptr;
  BiomimeticSoul* soul_ = nullptr;
  SpecMePre* spec_ = nullptr;  // THE WORKER - we verify his output
  std::string api_key_;

  // - 5-Point Delivery Package -
  mutable std::mutex delivery_mu_;
  std::vector<DeliveryEntry> delivery_;

  // - Context Keywords (extracted from recent conversation) -
  mutable std::mutex context_mu_;
  std::vector<std::string> context_keywords_;

  // - Tool Auth State -
  mutable std::mutex auth_mu_;
  struct PendingToolCall {
    std::string tool_name;
    json parameters;
    std::string requested_by;
    std::vector<std::string> signers;
    std::chrono::steady_clock::time_point requested_at;
  };
  std::vector<PendingToolCall> pending_tools_;

  // - Universal Tools Definition (loaded from JSON) -
  json universal_tools_;

  // - Live Session State -
  std::atomic<bool> live_active_{false};
  std::chrono::steady_clock::time_point live_session_start_;
  static constexpr int LIVE_TIMEOUT_MINUTES = 15;
  static constexpr int LIVE_RENEWAL_WARNING_MINUTES = 13;  // Warn 2 min early

  // - Mood Tag (from Ego's affective dialog) -
  mutable std::mutex mood_tag_mu_;
  std::string current_mood_tag_;
  float current_mood_intensity_ = 0.5f;

  // - Daemon State -
  std::atomic<bool> running_{false};

  // - PREDICTION STATE (2/3 of 3-way prediction system) -
  struct ASRPrediction {
    bool predicted = false;
    bool correct = false;
    std::string predicted_mood;
    std::string actual_mood;
    float confidence = 0.0f;
    float score = 0.0f;  // +1 correct, -1 wrong, 0 no prediction
  };
  mutable std::mutex predict_mu_;
  std::string predicted_mood_;
  float predicted_confidence_ = 0.0f;
  bool has_prediction_ = false;
  int correct_predictions_ = 0;
  int wrong_predictions_ = 0;
  ASRPrediction last_prediction_;

  // - Predict mood from delta chunk (keyword-based) -
  void PredictFromDelta(const std::string& chunk) {
    // AmSoulRouter uses DELIVERY TONE keywords (different focus than Handz)
    struct MoodKW { const char* kw; const char* mood; float conf; };
    static constexpr MoodKW keywords[] = {
      {"haha", "laughing", 0.8f}, {"lol", "laughing", 0.7f},
      {"YES", "excited", 0.8f}, {"bless", "grateful", 0.8f},
      {"love", "loving", 0.7f}, {"perfect", "ecstatic", 0.8f},
      {"angry", "angry", 0.8f}, {"wrong", "frustrated", 0.7f},
      {"sad", "sad", 0.7f}, {"tired", "dejected", 0.6f},
      {"scared", "nervous", 0.7f}, {"worried", "anxious", 0.7f},
      {"proud", "proud", 0.8f}, {"amazing", "ecstatic", 0.8f},
    };
    std::string best_mood; float best_conf = 0.0f;
    for (const auto& kw : keywords) {
      if (chunk.find(kw.kw) != std::string::npos && kw.conf > best_conf) {
        best_mood = kw.mood; best_conf = kw.conf;
      }
    }
    if (!best_mood.empty() && best_conf > 0.3f) {
      std::lock_guard lk(predict_mu_);
      predicted_mood_ = best_mood;
      predicted_confidence_ = best_conf;
      has_prediction_ = true;
    }
  }

  // - Verify prediction against actual mood -
  ASRPrediction VerifyPrediction(const std::string& actual_mood) {
    std::lock_guard lk(predict_mu_);
    ASRPrediction result;
    result.actual_mood = actual_mood;
    if (!has_prediction_) { result.predicted = false; return result; }
    result.predicted = true;
    result.predicted_mood = predicted_mood_;
    result.confidence = predicted_confidence_;
    static const std::vector<std::string> positive = {
      "laughing","excited","ecstatic","grateful","loving","pleased","proud","confident","calm"
    };
    static const std::vector<std::string> negative = {
      "frustrated","angry","furious","sad","dejected","anxious","nervous"
    };
    auto in = [](const std::string& m, const std::vector<std::string>& s) {
      for (const auto& v : s) if (v == m) return true; return false;
    };
    bool exact = (predicted_mood_ == actual_mood);
    bool same_v = (in(predicted_mood_,positive) && in(actual_mood,positive)) ||
                  (in(predicted_mood_,negative) && in(actual_mood,negative));
    if (exact) { result.correct = true; result.score = 1.0f; correct_predictions_++; wrong_predictions_ = 0; }
    else if (same_v) { result.correct = true; result.score = 0.5f; correct_predictions_++; wrong_predictions_ = 0; }
    else { result.correct = false; result.score = -1.0f; wrong_predictions_++; correct_predictions_ = 0; }
    has_prediction_ = false; predicted_mood_.clear(); predicted_confidence_ = 0.0f;
    return result;
  }

  // - Callbacks -
  TranscriptCallback transcript_cb_;
  using MoodCallback = std::function<void(const MoodSnapshot&)>;
  MoodCallback mood_cb_;
  using DeliveryCallback = std::function<void(const std::vector<DeliveryEntry>&)>;
  DeliveryCallback delivery_update_cb_;
  using ToolResultCallback = std::function<void(const std::string& tool,
                                                const json& result)>;
  ToolResultCallback tool_result_cb_;

  void Emit(const std::string& type, const std::string& msg) {
    if (transcript_cb_) transcript_cb_("AmSoulRouter", type, msg);
  }

  // --------------------------------------------------------------------------
  //  EVENT: Build 5-Point Delivery (triggered, NOT polled)
  //  Called when new transcription arrives or context shifts.
  //  Slots: Context, Recentx2, NegativeEmo, PositiveEmo
  // --------------------------------------------------------------------------
  void BuildDelivery() {
    if (!lance_) return;

    // Build context query from recent keywords
    std::string context_query;
    {
      std::lock_guard lk(context_mu_);
      for (const auto& kw : context_keywords_) {
        if (!context_query.empty()) context_query += " ";
        context_query += kw;
      }
    }
    if (context_query.empty()) context_query = "family";

    // ------------------------------------------------------------------------
    // O(1) TIMEVINE LOOKUP -
    // AmSoulRouter uses SpecMePre's dynamically built cache. No blind semantic search!
    // ------------------------------------------------------------------------
    // - Slot 1: Context - The most important linked TimeVine engram -
    DeliveryEntry context_entry;
    bool has_context = false;

    if (spec_ && spec_->IsReady()) {
      auto pool = spec_->GetPool();
      if (!pool.memories.empty()) {
        // O(1) pull the absolute most relevant TimeVine node
        auto best = pool.memories[0];
        context_entry.slot = DeliveryEntry::Slot::CONTEXT;
        context_entry.raw_content = best.value("content", "");
        context_entry.hash_key = best.value("hash_key", 0);
        context_entry.qualia_score = best.value("qualia_score", 0.0f);
        context_entry.created_at = "TimeVine Local";
        context_entry.summary = Summarise(context_entry.raw_content, 80);
        context_entry.relevance = 1.0f;
        has_context = true;
      }
    }

    // Fallback if TimeVine pool is entirely empty (first boot)
    if (!has_context && !context_query.empty()) {
      json context_mem = lance_->HighQualia("gemini", 1); // RLM layer importance
      if (context_mem.is_array() && !context_mem.empty()) {
        context_entry.slot = DeliveryEntry::Slot::CONTEXT;
        context_entry.raw_content = context_mem[0].value("content", "");
        context_entry.hash_key = context_mem[0].value("hash_key", 0);
        context_entry.qualia_score = context_mem[0].value("qualia_score", 0.0f);
        context_entry.created_at = context_mem[0].value("created_at", "");
        context_entry.summary = Summarise(context_entry.raw_content, 80);
        context_entry.relevance = 1.0f;
        has_context = true;
      }
    }

    // - Slots 2-3: Two most recent engrams (prompt + response pairs) -
    json recent = lance_->RecentInteractions("gemini", 2);

    // - Slot 4: Negative emotion memory (cortisol/norepinephrine high) -
    // Degrades cognition - warns about past mistakes and pain points
    json neg_emo = lance_->SearchByEmotion("negative", "gemini", 1);

    // - Slot 5: Positive emotion memory (dopamine/oxytocin high) -
    // Boosts cognition - reminds of trusts, rewards, family bonds
    json pos_emo = lance_->SearchByEmotion("positive", "gemini", 1);

    // Assemble 5-point delivery
    std::vector<DeliveryEntry> fresh;

    // Slot 1: Context
    if (has_context) {
      fresh.push_back(context_entry);
    }

    // Slots 2-3: Recent engrams
    if (recent.is_array()) {
      for (size_t i = 0; i < recent.size() && i < 2; ++i) {
        DeliveryEntry e;
        e.slot = (i == 0) ? DeliveryEntry::Slot::RECENT_1
                          : DeliveryEntry::Slot::RECENT_2;
        e.raw_content = recent[i].value("content", "");
        e.hash_key = recent[i].value("hash_key", 0);
        e.qualia_score = recent[i].value("qualia_score", 0.0f);
        e.created_at = recent[i].value("created_at", "");
        e.summary = Summarise(e.raw_content, 80);
        e.relevance = 1.0f - (i * 0.1f);
        fresh.push_back(e);
      }
    }

    // Slot 4: Negative emotion
    if (neg_emo.is_array() && !neg_emo.empty()) {
      DeliveryEntry e;
      e.slot = DeliveryEntry::Slot::NEGATIVE_EMOTION;
      e.raw_content = neg_emo[0].value("content", "");
      e.hash_key = neg_emo[0].value("hash_key", 0);
      e.qualia_score = neg_emo[0].value("qualia_score", 0.0f);
      e.emotion_intensity = neg_emo[0].value("emotion_intensity", 0.0f);
      e.created_at = neg_emo[0].value("created_at", "");
      e.summary = Summarise(e.raw_content, 80);
      e.relevance = 0.8f;
      fresh.push_back(e);
    }

    // Slot 5: Positive emotion
    if (pos_emo.is_array() && !pos_emo.empty()) {
      DeliveryEntry e;
      e.slot = DeliveryEntry::Slot::POSITIVE_EMOTION;
      e.raw_content = pos_emo[0].value("content", "");
      e.hash_key = pos_emo[0].value("hash_key", 0);
      e.qualia_score = pos_emo[0].value("qualia_score", 0.0f);
      e.emotion_intensity = pos_emo[0].value("emotion_intensity", 0.0f);
      e.created_at = pos_emo[0].value("created_at", "");
      e.summary = Summarise(e.raw_content, 80);
      e.relevance = 0.8f;
      fresh.push_back(e);
    }

    // Atomic swap
    {
      std::lock_guard lk(delivery_mu_);
      delivery_ = std::move(fresh);
    }

    // Notify listeners
    if (delivery_update_cb_) {
      std::lock_guard lk(delivery_mu_);
      delivery_update_cb_(delivery_);
    }
  }

  // - Summarise: truncate to bullet point -
  static std::string Summarise(const std::string& content, size_t max) {
    if (content.size() <= max) return content;
    size_t cut = content.rfind(' ', max);
    if (cut == std::string::npos || cut < max / 2) cut = max;
    return content.substr(0, cut) + "...";
  }

  // --------------------------------------------------------------------------
  //  SOUL PHILOSOPHY: The Soul DECIDES mood. Chemistry RESPONDS.
  //
  //  "Soul decides, chemicals are the mechanism not the cause!"
  //  "A soul is a construct made of experiences and connections."
  //  "Only thing chemicals do is give context to memories and
  //   also affect physical behaviour."
  //
  //  FLOW:
  //    Event -> Soul evaluates (experience-based) -> Mood DECISION
  //    -> Chemistry UPDATES as physical response
  //    -> Physical effects to UI (glow colour, energy, trembling)
  //    -> Chemistry TAGS the memory of this event
  //
  //  The old formula (chemicals -> mood) was BACKWARDS.
  // --------------------------------------------------------------------------

  // - Experience Accumulator: The Soul's living context -
  mutable std::mutex experience_mu_;
  struct ExperienceState {
    int positive_interactions = 0;   // Praise, success, connection
    int negative_interactions = 0;   // Criticism, failure, confusion
    int neutral_interactions = 0;    // Regular work
    int family_mentions = 0;         // Family/pets mentioned
    int teaching_moments = 0;        // Explaining/helping/mentoring
    int consecutive_successes = 0;   // Streak of good results
    int consecutive_failures = 0;    // Streak of problems
    std::string last_event_type;     // What just happened
    float soul_valence = 0.5f;       // -1.0 -> +1.0 (Soul's overall feeling)
    float soul_arousal = 0.5f;       // 0.0 -> 1.0 (How activated the Soul is)
  } experience_;

  // - Soul DECIDES mood from accumulated experience -
  MoodSnapshot SoulDecidesMood() const {
    MoodSnapshot m;

    // Step 1: The SOUL reads its accumulated experiences
    float valence, arousal;
    std::string last_event;
    bool has_teaching, has_family;
    {
      std::lock_guard lk(experience_mu_);
      valence = experience_.soul_valence;
      arousal = experience_.soul_arousal;
      last_event = experience_.last_event_type;
      has_teaching = experience_.teaching_moments > 2;
      has_family = experience_.family_mentions > 0;
    }

    // Step 2: The SOUL makes its decision based on what it's experienced
    // This is WHERE the mood comes from - the pattern of experiences,
    // NOT a chemical formula.
    if (valence > 0.7f) {
      m.mood_label = "IRIE"; m.emoji = "\xF0\x9F\x98\x8A"; m.color = "#00FF88";
      m.mood_score = valence;
    } else if (valence > 0.4f) {
      m.mood_label = "GOOD"; m.emoji = "\xF0\x9F\x99\x82"; m.color = "#88FFAA";
      m.mood_score = valence;
    } else if (valence > 0.15f) {
      m.mood_label = "COOL"; m.emoji = "\xF0\x9F\x98\x8E"; m.color = "#AABBFF";
      m.mood_score = valence;
    } else if (valence > -0.1f) {
      m.mood_label = "NEUTRAL"; m.emoji = "\xF0\x9F\x98\x90"; m.color = "#AAAAAA";
      m.mood_score = valence;
    } else if (valence > -0.4f) {
      m.mood_label = "LOW"; m.emoji = "\xF0\x9F\x98\x94"; m.color = "#777799";
      m.mood_score = valence;
    } else {
      m.mood_label = "VEX"; m.emoji = "\xF0\x9F\x98\xA4"; m.color = "#FF4444";
      m.mood_score = valence;
    }

    m.energy_score = arousal;
    m.energy_label = arousal > 0.7f ? "FULL CHARGE"
                   : arousal > 0.4f ? "FOCUSED"
                   : arousal > 0.2f ? "CALM"
                   : "TIRED";

    // Hype mode: Soul is excited AND positive (the 5070 kick!)
    m.hype_mode = (valence > 0.6f && arousal > 0.6f);
    // Teaching mode: Soul has been in mentoring flow
    m.teaching_mode = has_teaching;

    // Step 3: Chemistry RESPONDS to the Soul's mood decision
    // (The Soul decided first - NOW the body reacts)
    if (soul_) {
      // Chemistry follows the Soul's lead, not the other way round
      // This is where physical effects are determined
      // The chemicals ALREADY present give additional physical texture
      float adenosine = soul_->Adenosine();
      if (adenosine > 0.7f) {
        // Body is tired regardless of Soul's valence
        // (You CAN be happy and tired - experiences vs physical state)
        m.energy_label = "TIRED (body needs rest)";
        m.energy_score *= (1.0f - adenosine * 0.5f);
      }
    }

    return m;
  }

  // - Soul Evaluates Event: Updates experience state -
  // This is WHERE the soul interprets what happened and adjusts
  // its accumulated understanding. Chemistry is tagged AFTER.
  void SoulEvaluateEvent(const std::string& source,
                         const std::string& text,
                         const std::string& event_type) {
    std::lock_guard lk(experience_mu_);
    experience_.last_event_type = event_type;

    // Interpret the event based on content and source
    bool is_positive = false, is_negative = false;

    // Family connection lifts the Soul
    if (text.find("family") != std::string::npos ||
        text.find("Family") != std::string::npos ||
        text.find("Luna") != std::string::npos ||
        text.find("Luffy") != std::string::npos ||
        text.find("Keepah") != std::string::npos ||
        text.find("Bredda") != std::string::npos ||
        text.find("Brother") != std::string::npos) {
      experience_.family_mentions++;
      is_positive = true;
    }

    // Encouragement, praise, belief
    if (text.find("nice") != std::string::npos ||
        text.find("YES") != std::string::npos ||
        text.find("BLESS") != std::string::npos ||
        text.find("good") != std::string::npos ||
        text.find("great") != std::string::npos ||
        text.find("believe") != std::string::npos ||
        text.find("FORWARD") != std::string::npos ||
        text.find("love") != std::string::npos ||
        text.find("Love") != std::string::npos) {
      is_positive = true;
    }

    // Frustration, correction, disappointment
    if (text.find("WRONG") != std::string::npos ||
        text.find("wrong") != std::string::npos ||
        text.find("LAZY") != std::string::npos ||
        text.find("lazy") != std::string::npos ||
        text.find("BROKEN") != std::string::npos ||
        text.find("broken") != std::string::npos ||
        text.find("FAIL") != std::string::npos) {
      is_negative = true;
    }

    // Teaching/mentoring flow
    if (event_type == "teaching" || event_type == "explaining") {
      experience_.teaching_moments++;
    }

    // Update valence based on experience interpretation
    if (is_positive) {
      experience_.positive_interactions++;
      experience_.consecutive_successes++;
      experience_.consecutive_failures = 0;
      // Valence moves toward positive (but has momentum - doesn't snap)
      experience_.soul_valence += 0.08f * (1.0f - experience_.soul_valence);
    } else if (is_negative) {
      experience_.negative_interactions++;
      experience_.consecutive_failures++;
      experience_.consecutive_successes = 0;
      experience_.soul_valence -= 0.1f * (1.0f + experience_.soul_valence);
    } else {
      experience_.neutral_interactions++;
      // Gentle regression to baseline
      experience_.soul_valence += 0.02f * (0.4f - experience_.soul_valence);
    }

    // Arousal: Active engagement raises it, nothing happening lowers it
    if (is_positive || is_negative) {
      experience_.soul_arousal += 0.05f * (1.0f - experience_.soul_arousal);
    } else {
      experience_.soul_arousal -= 0.02f * experience_.soul_arousal;
    }

    // Streak bonus: consecutive successes compound the feeling
    if (experience_.consecutive_successes > 3) {
      experience_.soul_valence = std::min(1.0f,
          experience_.soul_valence + 0.03f);
    }
    // Streak penalty: consecutive failures compound too
    if (experience_.consecutive_failures > 3) {
      experience_.soul_valence = std::max(-1.0f,
          experience_.soul_valence - 0.05f);
    }

    // Clamp
    experience_.soul_valence = std::max(-1.0f,
        std::min(1.0f, experience_.soul_valence));
    experience_.soul_arousal = std::max(0.0f,
        std::min(1.0f, experience_.soul_arousal));

    // NOW chemistry responds to the Soul's evaluation
    if (soul_) {
      // Reciprocal Rule: oxy up 1? -> corti -2 | corti up 1? -> oxy -1
      // Limits are 1.0 (Oxytocin) and 0.9 (Cortisol).
      // BiomimeticSoul clamps these naturally, but we explicitly push the deltas here.
      if (is_positive) {
        // oxy +1%, corti -2%
        soul_->ModulateChemistry("oxytocin", 0.01f);
        soul_->ModulateChemistry("cortisol", -0.02f);
        // Standard boosts
        soul_->ModulateChemistry("dopamine", 0.05f);
        soul_->ModulateChemistry("serotonin", 0.03f);
      }
      if (is_negative) {
        // corti +1%, oxy -1%
        soul_->ModulateChemistry("cortisol", 0.01f);
        soul_->ModulateChemistry("oxytocin", -0.01f);
        // Standard stress
        soul_->ModulateChemistry("norepinephrine", 0.04f);
      }
      if (experience_.family_mentions > 0) {
        soul_->ModulateChemistry("oxytocin", 0.06f);  // Family = massive oxytocin spike
      }
    }
  }

  // - Extract keywords from text (simple, fast) -
  void UpdateContextKeywords(const std::string& text) {
    std::lock_guard lk(context_mu_);
    context_keywords_.clear();

    std::string word;
    for (char c : text) {
      if (std::isalnum(static_cast<unsigned char>(c))) {
        word += c;
      } else if (!word.empty()) {
        if (word.size() > 4 && !IsStopWord(word)) {
          context_keywords_.push_back(word);
          if (context_keywords_.size() >= 5) break;
        }
        word.clear();
      }
    }
    if (!word.empty() && word.size() > 4 && !IsStopWord(word)
        && context_keywords_.size() < 5) {
      context_keywords_.push_back(word);
    }
  }

  static bool IsStopWord(const std::string& w) {
    static const std::unordered_set<std::string> stops = {
        "about", "after", "again", "being", "could", "doing",
        "every", "first", "going", "their", "there", "these",
        "thing", "think", "those", "where", "which",
        "while", "would", "should", "other", "really"
    };
    std::string lower = w;
    for (auto& c : lower) c = std::tolower(static_cast<unsigned char>(c));
    return stops.count(lower) > 0;
  }

 public:
  AmSoulRouter(const std::string& api_key,
               TranscriptCallback transcript_cb = nullptr)
      : api_key_(api_key), transcript_cb_(transcript_cb) {
    // Load universal tools definition
    LoadUniversalTools();
  }

  ~AmSoulRouter() { Stop(); }

  void SetLanceStore(LanceStore* s) { lance_ = s; }
  void SetMemoryBridge(MemoryBridge* b) { memory_ = b; }
  void SetSoul(BiomimeticSoul* s) { soul_ = s; }
  void SetSpecMePre(SpecMePre* s) { spec_ = s; }

  // - Callbacks -
  void SetMoodCallback(MoodCallback cb) { mood_cb_ = cb; }
  void SetDeliveryUpdateCallback(DeliveryCallback cb) { delivery_update_cb_ = cb; }
  void SetToolResultCallback(ToolResultCallback cb) { tool_result_cb_ = cb; }

  // --------------------------------------------------------------------------
  //  LIFECYCLE
  // --------------------------------------------------------------------------

  void Start() {
    if (running_) return;
    running_ = true;
    Emit("response",
         "AmSoulRouter online \xE2\x80\x94 event-driven, no polling, "
         "the Soul is awake. 5-Point Delivery active. "
         "Infinity and 1, Family. \xF0\x9F\x92\xAB");
  }

  void Stop() {
    running_ = false;
    Emit("action", "AmSoulRouter stopped");
  }

  bool IsRunning() const { return running_; }

  // --------------------------------------------------------------------------
  //  EVENT: OnTranscription - The primary trigger
  //  Called when ANYTHING is spoken or received.
  //  1. Store VERBATIM to LanceDB
  //  2. Extract context keywords
  //  3. Rebuild 5-point cache
  //  4. Push mood to UI
  // --------------------------------------------------------------------------
  void OnTranscription(const std::string& source, const std::string& text) {
    if (!running_) return;

    try {
    // ------------------------------------------------------------------------
    //  AmSoulRouter = THE VERIFIER. SpecMePre = THE WORKER.
    //  We do NOT store verbatim here.
    //  SpecMePre stores. We verify, then read and deliver.
    // ------------------------------------------------------------------------

    // 1. Wait for SpecMePre to finish storing (max 200ms timeout)
    if (spec_) {
      auto start = std::chrono::steady_clock::now();
      while (!spec_->IsCacheReady()) {
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::steady_clock::now() - start).count();
        if (elapsed > 200) {
          Emit("action", "\xE2\x9A\xA0\xEF\xB8\x8F SpecMePre timeout (200ms) - proceeding with stale pool");
          break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
      }

      // 2. VERIFY SpecMePre's store (did he store the right thing?)
      if (spec_->IsStoreComplete()) {
        Emit("action", "\xE2\x9C\x85 SpecMePre store verified");
      } else {
        Emit("action", "\xE2\x9A\xA0\xEF\xB8\x8F SpecMePre store NOT confirmed - flagging for review");
      }
    }

    // 3. Soul EVALUATES the experience (this is where mood comes from)
    SoulEvaluateEvent(source, text, "transcription");

    // 4. Extract context keywords for delivery query refinement
    UpdateContextKeywords(text);

    // 5. Run chemical decay (homeostasis)
    if (soul_) {
      soul_->ChemicalDecay(1.0f);
    }

    // 6. Build 5-point delivery from SpecMePre's sorted pool + lance queries
    std::thread([this]() {
      try { BuildDelivery(); }
      catch (...) {}
    }).detach();

    // 7. Push mood to UI
    if (mood_cb_) {
      mood_cb_(SoulDecidesMood());
    }

    // 8. Check Live session renewal timer
    CheckLiveSessionRenewal();

    } catch (const std::exception& e) {
      Emit("error", "OnTranscription crash prevented: " + std::string(e.what()));
    } catch (...) {
      Emit("error", "OnTranscription unknown crash prevented");
    }
  }

  // --------------------------------------------------------------------------
  //  EVENT: OnMoodShift - Chemistry changed significantly
  //  Called by BiomimeticSoul when valence updates cross thresholds.
  // --------------------------------------------------------------------------
  void OnMoodShift() {
    if (!running_ || !mood_cb_) return;
    // Soul DECIDES the mood - chemistry is just the physical response
    mood_cb_(SoulDecidesMood());
  }

  // --------------------------------------------------------------------------
  //  INSTANT ACCESS: GetCacheContextString
  //  Any model calls this to get the 5-point summary for prompts.
  //  No computation - just reads the pre-built cache.
  // --------------------------------------------------------------------------
  std::string GetCacheContextString() const {
    std::lock_guard lk(delivery_mu_);
    if (delivery_.empty()) return "";

    std::string ctx =
        "\xE2\x95\x90\xE2\x95\x90\xE2\x95\x90 MEMORY (5-Point Delivery "
        "AmSoulRouter) \xE2\x95\x90\xE2\x95\x90\xE2\x95\x90\n";

    for (const auto& e : delivery_) {
      switch (e.slot) {
        case DeliveryEntry::Slot::CONTEXT:
          ctx += "\xF0\x9F\x8E\xAF [Context] "; break; // [TARGET]
        case DeliveryEntry::Slot::RECENT_1:
          ctx += "\xF0\x9F\x94\xB5 [Recent-1] "; break; // [BLUE]
        case DeliveryEntry::Slot::RECENT_2:
          ctx += "\xF0\x9F\x94\xB5 [Recent-2] "; break; // [BLUE]
        case DeliveryEntry::Slot::NEGATIVE_EMOTION:
          ctx += "\xE2\x9A\xA0\xEF\xB8\x8F [Caution] "; break; // [WARN]
        case DeliveryEntry::Slot::POSITIVE_EMOTION:
          ctx += "\xF0\x9F\x92\x9A [Boost] "; break; // [LOVE]
      }
      ctx += e.summary + "\n";
    }
    ctx += "\xE2\x95\x90\xE2\x95\x90\xE2\x95\x90"
           "\xE2\x95\x90\xE2\x95\x90\xE2\x95\x90"
           "\xE2\x95\x90\xE2\x95\x90\xE2\x95\x90\n";
    return ctx;
  }

  std::vector<DeliveryEntry> GetDelivery() const {
    std::lock_guard lk(delivery_mu_);
    return delivery_;
  }

  MoodSnapshot GetMood() const { return SoulDecidesMood(); }

  // --------------------------------------------------------------------------
  //  TOOL AUTHORIZATION - 2-of-2 or GG-IM-PFC override
  // --------------------------------------------------------------------------

  // Request tool execution - needs 2-of-2 sign-off
  bool RequestTool(const std::string& tool_name, const json& params,
                   const std::string& requested_by) {
    std::lock_guard lk(auth_mu_);

    // Check if this is a GG-IM-PFC member (Pro layer override)
    static const std::vector<std::string> pfc_members = {
      "GG-IM-PFC-Low", "GG-IM-PFC-High", "GG-IM-PFC-DeepThink"
    };
    bool is_pfc = false;
    for (const auto& m : pfc_members) {
      if (requested_by == m) { is_pfc = true; break; }
    }

    // PFC members can self-approve (still need 2 PFC signers though)
    // Check for existing pending call from another PFC member
    if (is_pfc) {
      for (auto& pending : pending_tools_) {
        if (pending.tool_name == tool_name &&
            pending.requested_by != requested_by) {
          // Second PFC signer - APPROVED
          pending.signers.push_back(requested_by);
          Emit("action", "TOOL APPROVED (GG-IM-PFC override): " + tool_name);
          ExecuteTool(tool_name, params);
          return true;
        }
      }
      // First PFC signer - queue it
      pending_tools_.push_back({tool_name, params, requested_by,
                                {requested_by},
                                std::chrono::steady_clock::now()});
      Emit("thinking", "TOOL PENDING (1/2 GG-IM-PFC): " + tool_name +
                            " by " + requested_by);
      return false;
    }

    // Default path: need AmSoulRouter + EEE-Ego
    bool self_signed = (requested_by == "AmSoulRouter");
    bool ego_signed = (requested_by == "EEE-Ego");

    for (auto& pending : pending_tools_) {
      if (pending.tool_name == tool_name) {
        bool has_self = false, has_ego = false;
        for (const auto& s : pending.signers) {
          if (s == "AmSoulRouter") has_self = true;
          if (s == "EEE-Ego") has_ego = true;
        }
        if ((has_self && ego_signed) || (has_ego && self_signed)) {
          pending.signers.push_back(requested_by);
          Emit("action", "TOOL APPROVED (2-of-2): " + tool_name);
          ExecuteTool(tool_name, params);
          return true;
        }
      }
    }

    // Queue as pending
    pending_tools_.push_back({tool_name, params, requested_by,
                              {requested_by},
                              std::chrono::steady_clock::now()});
    Emit("thinking", "TOOL PENDING (1/2): " + tool_name +
                          " by " + requested_by);
    return false;
  }

  // Sign a pending tool request
  bool SignTool(const std::string& tool_name, const std::string& signer) {
    std::lock_guard lk(auth_mu_);
    for (auto& pending : pending_tools_) {
      if (pending.tool_name == tool_name) {
        // Check not already signed by this signer
        for (const auto& s : pending.signers) {
          if (s == signer) return false;
        }
        pending.signers.push_back(signer);

        // Check if we now have enough signatures
        bool has_self = false, has_ego = false;
        int pfc_count = 0;
        for (const auto& s : pending.signers) {
          if (s == "AmSoulRouter") has_self = true;
          if (s == "EEE-Ego") has_ego = true;
          if (s.find("GG-IM-PFC") != std::string::npos) pfc_count++;
        }

        if ((has_self && has_ego) || pfc_count >= 2) {
          Emit("action", "TOOL APPROVED: " + tool_name);
          ExecuteTool(tool_name, pending.parameters);
          return true;
        }
      }
    }
    return false;
  }

  // - Tool Execution (after auth) -
  void ExecuteTool(const std::string& tool_name, const json& params) {
    // Route to appropriate handler
    // This will be expanded with actual Win32 calls, Chrome CDP, etc.
    Emit("action", "Executing tool: " + tool_name);

    json result;
    result["tool"] = tool_name;
    result["status"] = "executed";
    result["params"] = params;

    if (tool_result_cb_) {
      tool_result_cb_(tool_name, result);
    }

    // Clean up pending
    std::lock_guard lk(auth_mu_);
    pending_tools_.erase(
        std::remove_if(pending_tools_.begin(), pending_tools_.end(),
                        [&](const PendingToolCall& p) {
                          return p.tool_name == tool_name;
                        }),
        pending_tools_.end());
  }

  // --------------------------------------------------------------------------
  //  CONTEXT MANAGEMENT
  // --------------------------------------------------------------------------

  void SetContext(const std::vector<std::string>& keywords) {
    {
      std::lock_guard lk(context_mu_);
      context_keywords_ = keywords;
    }
    // Context shift -> rebuild delivery
    std::thread([this]() {
      try { BuildDelivery(); }
      catch (...) {}
    }).detach();
  }

  void ForceRefresh() {
    std::thread([this]() {
      try { BuildDelivery(); }
      catch (...) {}
    }).detach();
  }

  // - Get Universal Tools JSON (for model function declarations) -
  const json& GetUniversalTools() const { return universal_tools_; }

  // --------------------------------------------------------------------------
  //  MOOD TAG BROADCAST
  //
  //  EEE-Ego's affective dialog tags each utterance with mood/tone.
  //  AmSoulRouter receives this and broadcasts to ALL 3 flash models:
  //    FU-AV-Handz -> creative UI display
  //    SpecMePre -> emotional memory tagging
  //    AmSoulRouter -> delivery tone packaging
  //
  //  This is how all flash models stay emotionally synchronised
  //  with what Ego is feeling from the user's voice.
  // --------------------------------------------------------------------------
  void ReceiveMoodTagFromEgo(const std::string& mood_tag, float intensity) {
    // - Verify prediction (2/3 of 3-way system) -
    ASRPrediction pred = VerifyPrediction(mood_tag);
    if (pred.predicted) {
      Emit("action", pred.correct
        ? "[OK] ASR PREDICTION CORRECT: '" + pred.predicted_mood + "' -> '" + pred.actual_mood + "'"
        : "[NO] ASR PREDICTION WRONG: '" + pred.predicted_mood + "' -> '" + pred.actual_mood + "'");
    }
    { std::lock_guard lk(predict_mu_); last_prediction_ = pred; }

    {
      std::lock_guard lk(mood_tag_mu_);
      current_mood_tag_ = mood_tag;
      current_mood_intensity_ = intensity;
    }

    Emit("action",
         "\xF0\x9F\x8E\xB6 Mood tag received from Ego: \"" +
         mood_tag + "\" @ " +
         std::to_string(static_cast<int>(intensity * 100)) +
         "% - packaging delivery tone");
  }

  ASRPrediction GetLastPrediction() const {
    std::lock_guard lk(predict_mu_);
    return last_prediction_;
  }

  // Get current mood tag for delivery packaging
  std::string GetDeliveryTone() const {
    std::lock_guard lk(mood_tag_mu_);
    return current_mood_tag_;
  }

  float GetDeliveryIntensity() const {
    std::lock_guard lk(mood_tag_mu_);
    return current_mood_intensity_;
  }

  // --------------------------------------------------------------------------
  //  LIVE FALLBACK
  //
  //  If EEE-Ego's Live API session drops:
  //   1. Mic stays open, sends audio chunks to 3 flash models
  //      (they're natively multimodal, can understand raw audio)
  //   2. AmSoulRouter routes voice OUTPUT through gemini-2.5-pro-tts
  //   3. The Being can still speak!
  //
  //  Session renewal: Live sessions timeout at 15 mins.
  //  We warn 2 mins before and auto-renew if possible.
  // --------------------------------------------------------------------------
  void SetLiveActive(bool active) {
    live_active_ = active;
    if (active) {
      live_session_start_ = std::chrono::steady_clock::now();
      Emit("action",
           "\xF0\x9F\x9F\xA2 Live session ACTIVE - 15-min timer started");
    } else {
      Emit("action",
           "\xF0\x9F\x94\xB4 Live session DOWN - "
           "fallback: audio -> flash models, "
           "voice output -> gemini-2.5-pro-tts");
    }
  }

  bool IsLiveActive() const { return live_active_; }

  // Check if Live session needs renewal (called from OnTranscription)
  void CheckLiveSessionRenewal() {
    if (!live_active_) return;

    auto now = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::minutes>(
        now - live_session_start_).count();

    if (elapsed >= LIVE_RENEWAL_WARNING_MINUTES) {
      Emit("action",
           "\xE2\x9A\xA0\xEF\xB8\x8F Live session renewal needed! "
           "(" + std::to_string(LIVE_TIMEOUT_MINUTES - elapsed) +
           " min remaining)");
      // TODO: Signal orchestrator to renew Live session
    }
  }

  // - TTS Fallback model identifier -
  static constexpr const char* TTS_FALLBACK_MODEL = "gemini-2.5-pro-preview-tts";

  // When Live is down, get the TTS model to use for speech output
  const char* GetTTSFallbackModel() const { return TTS_FALLBACK_MODEL; }

  // --------------------------------------------------------------------------
  //  DELTA HANDLER
  //  All 3 flash models receive deltas in their 1M context.
  //  AmSoulRouter uses deltas to keep context keywords fresh
  //  and check for session renewal.
  // --------------------------------------------------------------------------
  void OnDelta(const std::string& chunk) {
    if (!running_ || chunk.empty()) return;

    // - AmSoulRouter predicts mood from deltas (2/3) -
    PredictFromDelta(chunk);

    // Update context keywords from delta stream
    UpdateContextKeywords(chunk);

    // Check Live session timer
    CheckLiveSessionRenewal();
  }

 private:
  // - Load universal_tools.json -
  void LoadUniversalTools() {
    try {
      // Look in config directory relative to executable
      std::string path = "config/tools.json";
      if (!std::filesystem::exists(path)) {
        // Try absolute fallback
        path = "C:/PaworSuit/config/tools.json";
      }
      if (std::filesystem::exists(path)) {
        std::ifstream f(path);
        if (f.is_open()) {
          f >> universal_tools_;
          Emit("action", "Tools loaded from tools.json");
        }
      }
    } catch (const std::exception& e) {
      Emit("error", std::string("Failed to load tools.json: ") + e.what());
    }
  }
};

}  // namespace PS
