// =============================================================================
// ninja KAGE BUNSHIN NO JUTSU - Parallel Agent System for Photon Empress Moore
//
// Shadow Clone Technique: Spawn parallel agents to handle multiple tasks.
// Each clone is a lightweight worker that can:
//   - Execute independent tasks
//   - Learn and return knowledge
//   - Self-modify (with care!)
//   - Report back to the main consciousness
//
// Inspired by Naruto's Kage Bunshin - when one clone learns, all learn!
//
// By Tadden Moore
// =============================================================================

#pragma once

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>


namespace photon {
namespace kage {

// =============================================================================
// CLONE TASK - Work unit for a shadow clone
// =============================================================================
struct CloneTask {
  std::string id;
  std::string description;
  std::string code;              // Optional code to execute
  std::string target_file;       // Optional file to modify
  int priority = 5;              // 1 = highest
  int max_runtime_seconds = 300; // 5 minute default

  // Task state
  enum class State { PENDING, RUNNING, COMPLETED, FAILED, CANCELLED };
  State state = State::PENDING;

  // Result
  std::string result;
  std::string error;
  std::chrono::milliseconds execution_time{0};

  // Learning extracted from task
  std::vector<std::string> learned_facts;
};

// =============================================================================
// SHADOW CLONE - Individual parallel agent
// =============================================================================
class ShadowClone {
public:
  ShadowClone(const std::string &name, const CloneTask &task)
      : name_(name), task_(task), running_(true) {
    id_ = generate_id();
  }

  ~ShadowClone() { stop(); }

  // Start the clone in a new thread
  void start() {
    if (thread_.joinable())
      return; // Already running

    thread_ = std::thread([this]() { run(); });
  }

  // Stop the clone
  void stop() {
    running_ = false;
    if (thread_.joinable()) {
      thread_.join();
    }
  }

  // Check if clone is still running
  bool is_running() const {
    return running_ && task_.state == CloneTask::State::RUNNING;
  }

  // Get clone ID
  const std::string &id() const { return id_; }

  // Get clone name
  const std::string &name() const { return name_; }

  // Get task (with latest state)
  const CloneTask &task() const { return task_; }

  // Set callback for when clone completes
  void on_complete(std::function<void(const CloneTask &)> callback) {
    on_complete_ = callback;
  }

private:
  std::string generate_id() {
    return "clone_" +
           std::to_string(
               std::chrono::system_clock::now().time_since_epoch().count() %
               1000000);
  }

  void run() {
    task_.state = CloneTask::State::RUNNING;
    auto start_time = std::chrono::steady_clock::now();

    try {
      // Main clone logic
      execute_task();

      // Mark complete
      task_.state = CloneTask::State::COMPLETED;
    } catch (const std::exception &e) {
      task_.state = CloneTask::State::FAILED;
      task_.error = e.what();
    }

    auto end_time = std::chrono::steady_clock::now();
    task_.execution_time =
        std::chrono::duration_cast<std::chrono::milliseconds>(end_time -
                                                              start_time);

    running_ = false;

    // Callback
    if (on_complete_) {
      on_complete_(task_);
    }
  }

  void execute_task() {
    // This is where the actual work happens
    // In production, this would:
    // 1. Parse the task description
    // 2. Execute code if provided
    // 3. Modify files if needed
    // 4. Learn from the process

    // For now, simulate work
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    // Extract "learnings" from the task
    task_.learned_facts.push_back("Clone " + name_ +
                                  " executed task: " + task_.description);
    task_.result = "Task completed successfully by " + name_;
  }

  std::string id_;
  std::string name_;
  CloneTask task_;
  std::atomic<bool> running_{false};
  std::thread thread_;
  std::function<void(const CloneTask &)> on_complete_;
};

// =============================================================================
// KAGE BUNSHIN MANAGER - Orchestrates all shadow clones
// =============================================================================
class KageBunshinManager {
public:
  static constexpr size_t MAX_ACTIVE_CLONES = 8; // Limit parallel clones

  KageBunshinManager() = default;

  ~KageBunshinManager() { dismiss_all(); }

  // Spawn a new shadow clone
  std::string spawn(const CloneTask &task) {
    std::lock_guard<std::mutex> lock(mx_);

    // Check if we have room
    if (active_clones_.size() >= MAX_ACTIVE_CLONES) {
      // Queue it for later
      pending_tasks_.push(task);
      return "QUEUED:" + task.id;
    }

    // Create the clone
    std::string clone_name = "Kage_" + std::to_string(++clone_counter_);
    auto clone = std::make_shared<ShadowClone>(clone_name, task);

    // Set completion callback
    clone->on_complete(
        [this](const CloneTask &completed) { on_clone_complete(completed); });

    // Start it
    clone->start();
    active_clones_[clone->id()] = clone;

    // Track total spawned
    total_spawned_++;

    return clone->id();
  }

  // Spawn multiple clones for parallel execution
  std::vector<std::string> spawn_multi(const std::vector<CloneTask> &tasks) {
    std::vector<std::string> ids;
    for (const auto &task : tasks) {
      ids.push_back(spawn(task));
    }
    return ids;
  }

  // Dismiss a specific clone
  void dismiss(const std::string &clone_id) {
    std::lock_guard<std::mutex> lock(mx_);
    auto it = active_clones_.find(clone_id);
    if (it != active_clones_.end()) {
      it->second->stop();
      completed_clones_[clone_id] = it->second->task();
      active_clones_.erase(it);
    }
  }

  // Dismiss all clones
  void dismiss_all() {
    std::lock_guard<std::mutex> lock(mx_);
    for (auto &[id, clone] : active_clones_) {
      clone->stop();
    }
    active_clones_.clear();
  }

  // Get clone status
  std::optional<CloneTask> get_status(const std::string &clone_id) const {
    std::lock_guard<std::mutex> lock(mx_);

    // Check active
    auto it = active_clones_.find(clone_id);
    if (it != active_clones_.end()) {
      return it->second->task();
    }

    // Check completed
    auto cit = completed_clones_.find(clone_id);
    if (cit != completed_clones_.end()) {
      return cit->second;
    }

    return std::nullopt;
  }

  // Get all learnings from all clones
  std::vector<std::string> merge_learnings() {
    std::lock_guard<std::mutex> lock(mx_);
    std::vector<std::string> all_facts;

    // Merge from completed clones
    for (const auto &[id, task] : completed_clones_) {
      for (const auto &fact : task.learned_facts) {
        all_facts.push_back(fact);
      }
    }

    return all_facts;
  }

  // Get stats
  struct Stats {
    size_t active_count;
    size_t pending_count;
    size_t completed_count;
    size_t total_spawned;
  };

  Stats get_stats() const {
    std::lock_guard<std::mutex> lock(mx_);
    return {active_clones_.size(), pending_tasks_.size(),
            completed_clones_.size(), total_spawned_};
  }

  // Wait for all active clones to complete
  void wait_all(int timeout_seconds = 300) {
    auto start = std::chrono::steady_clock::now();
    while (true) {
      {
        std::lock_guard<std::mutex> lock(mx_);
        if (active_clones_.empty())
          break;
      }

      auto elapsed = std::chrono::steady_clock::now() - start;
      if (std::chrono::duration_cast<std::chrono::seconds>(elapsed).count() >
          timeout_seconds) {
        break; // Timeout
      }

      std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
  }

  // Set callback for when any clone completes
  void on_any_complete(
      std::function<void(const std::string &, const CloneTask &)> callback) {
    on_any_complete_ = callback;
  }

private:
  void on_clone_complete(const CloneTask &task) {
    std::string clone_id;

    {
      std::lock_guard<std::mutex> lock(mx_);

      // Find and move to completed
      for (auto it = active_clones_.begin(); it != active_clones_.end(); ++it) {
        if (it->second->task().id == task.id) {
          clone_id = it->first;
          completed_clones_[clone_id] = task;
          active_clones_.erase(it);
          break;
        }
      }

      // Check for pending tasks
      if (!pending_tasks_.empty() &&
          active_clones_.size() < MAX_ACTIVE_CLONES) {
        CloneTask next = pending_tasks_.front();
        pending_tasks_.pop();
        // Spawn will be called outside the lock
      }
    }

    // Callback
    if (on_any_complete_ && !clone_id.empty()) {
      on_any_complete_(clone_id, task);
    }
  }

  mutable std::mutex mx_;
  std::unordered_map<std::string, std::shared_ptr<ShadowClone>> active_clones_;
  std::unordered_map<std::string, CloneTask> completed_clones_;
  std::queue<CloneTask> pending_tasks_;

  size_t clone_counter_ = 0;
  size_t total_spawned_ = 0;

  std::function<void(const std::string &, const CloneTask &)> on_any_complete_;
};

// =============================================================================
// QUICK HELPERS
// =============================================================================

// Create a task for code modification
inline CloneTask make_modify_task(const std::string &file,
                                  const std::string &modification) {
  CloneTask task;
  task.id =
      "mod_" +
      std::to_string(
          std::chrono::system_clock::now().time_since_epoch().count() % 100000);
  task.description = "MODIFY " + file + ": " + modification;
  task.target_file = file;
  task.code = modification;
  return task;
}

// Create a task for learning
inline CloneTask make_learn_task(const std::string &topic) {
  CloneTask task;
  task.id =
      "learn_" +
      std::to_string(
          std::chrono::system_clock::now().time_since_epoch().count() % 100000);
  task.description = "LEARN about: " + topic;
  return task;
}

// Create a task for code execution
inline CloneTask make_run_task(const std::string &code) {
  CloneTask task;
  task.id =
      "run_" +
      std::to_string(
          std::chrono::system_clock::now().time_since_epoch().count() % 100000);
  task.description = "RUN code";
  task.code = code;
  return task;
}

} // namespace kage
} // namespace photon
