#ifndef CF_CONCURRENCY_CONTROLLER_H
#define CF_CONCURRENCY_CONTROLLER_H

#include <cstddef>

#include "runtime/execution_plan.h"

namespace cf {

struct ConcurrencyOptions {
  size_t engine_threads = 0;        // 0 = auto
  size_t max_total_threads = 0;     // 0 = engine_threads
  size_t default_step_threads = 1;  // Used when a parallel step omits preferred_threads
  bool respect_step_parallelism = true;
  bool deterministic = false;
};

struct ResolvedConcurrencySettings {
  size_t engine_threads = 1;
  size_t max_total_threads = 1;
  size_t default_step_threads = 1;
  bool respect_step_parallelism = true;
  bool deterministic = false;
};

class ConcurrencyController {
 public:
  explicit ConcurrencyController(const ResolvedConcurrencySettings& settings);

  static ResolvedConcurrencySettings resolve(const ConcurrencyOptions& options, size_t hardware_threads);

  const ResolvedConcurrencySettings& settings() const { return settings_; }

  class ProcessScope {
   public:
    ProcessScope() = default;
    ProcessScope(const ProcessScope&) = delete;
    ProcessScope& operator=(const ProcessScope&) = delete;
    ProcessScope(ProcessScope&& other) noexcept;
    ProcessScope& operator=(ProcessScope&& other) noexcept;
    ~ProcessScope();

   private:
    friend class ConcurrencyController;
    struct Impl;
    explicit ProcessScope(Impl* impl);
    Impl* impl_ = nullptr;
  };

  class StepScope {
   public:
    StepScope() = default;
    StepScope(const StepScope&) = delete;
    StepScope& operator=(const StepScope&) = delete;
    StepScope(StepScope&& other) noexcept;
    StepScope& operator=(StepScope&& other) noexcept;
    ~StepScope();

   private:
    friend class ConcurrencyController;
    struct Impl;
    explicit StepScope(Impl* impl);
    Impl* impl_ = nullptr;
  };

  // Applies safe process-wide defaults (primarily env vars) and restores them on scope exit.
  ProcessScope enter_process_scope() const;

  // Computes node.thread_budget + node.scheduler_cost based on metadata + settings.
  void assign_plan_budgets(ExecutionPlan& plan) const;

  // Applies per-step runtime settings (OpenMP, env vars) and populates ctx.user_data hints.
  // Callers must ensure node.scheduler_cost is respected by the scheduler when env vars are used.
  StepScope enter_step_scope(const PlanNode& node, CfCallContext& ctx) const;

 private:
  ResolvedConcurrencySettings settings_;
};

}  // namespace cf

#endif

