#include <chrono>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#include "compiler/jsonld_loader.h"
#include "compiler/pipeline_parser.h"
#include "compiler/plan_builder.h"
#include "plugin_loader/plugin_loader.h"
#include "scheduler/scheduler.h"

namespace {

void print_usage() {
  std::cout << "Usage: cf_pipeline_v2 --pipeline <file> --steps <file> [--steps <file> ...] --plugins <dir> [--plugins <dir> ...] [--plugins-recursive]\n"
               "                      [--engine-threads N|--threads N] [--max-total-threads M] [--default-step-threads K]\n"
               "                      [--respect-step-parallelism true|false] [--deterministic [true|false]]\n"
               "                      [--interval S] [--iterations N] [--duration S] [--debug]\n";
}

void debug_check(bool enabled, const std::string& label, const std::string& detail = "") {
  if (!enabled) return;
  std::cout << "[OK] " << label;
  if (!detail.empty()) {
    std::cout << " - " << detail;
  }
  std::cout << "\n";
}

}  // namespace

int main(int argc, char** argv) {
  std::string pipeline_path;
  std::vector<std::string> step_files;
  std::vector<std::string> plugin_dirs;
  size_t engine_threads = 0;
  size_t max_total_threads = 0;
  size_t default_step_threads = 1;
  bool respect_step_parallelism = true;
  bool deterministic = false;
  bool plugins_recursive = false;
  double interval = 0.0;
  int iterations = 1;
  double duration = 0.0;
  bool debug = false;

  auto env_enabled = [](const char* raw) -> bool {
    if (!raw) {
      return false;
    }
    std::string text = raw;
    for (char& c : text) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
    return text == "1" || text == "true" || text == "yes" || text == "on";
  };

  auto parse_bool = [](const std::string& value, bool& out) -> bool {
    std::string v = value;
    for (char& c : v) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
    if (v == "1" || v == "true" || v == "yes" || v == "on") {
      out = true;
      return true;
    }
    if (v == "0" || v == "false" || v == "no" || v == "off") {
      out = false;
      return true;
    }
    return false;
  };

  auto parse_optional_bool_arg = [&](int& i, bool& out) {
    if (i + 1 < argc) {
      std::string next = argv[i + 1];
      if (next.rfind("--", 0) != 0) {
        bool parsed = out;
        if (parse_bool(next, parsed)) {
          out = parsed;
          ++i;
          return;
        }
      }
    }
    out = true;
  };

  for (int i = 1; i < argc; ++i) {
    std::string arg = argv[i];
    if (arg == "--pipeline" && i + 1 < argc) {
      pipeline_path = argv[++i];
    } else if (arg == "--steps" && i + 1 < argc) {
      step_files.push_back(argv[++i]);
    } else if (arg == "--plugins" && i + 1 < argc) {
      plugin_dirs.push_back(argv[++i]);
    } else if (arg == "--plugins-recursive") {
      plugins_recursive = true;
    } else if ((arg == "--engine-threads" || arg == "--threads") && i + 1 < argc) {
      engine_threads = static_cast<size_t>(std::stoul(argv[++i]));
    } else if (arg == "--max-total-threads" && i + 1 < argc) {
      max_total_threads = static_cast<size_t>(std::stoul(argv[++i]));
    } else if (arg == "--default-step-threads" && i + 1 < argc) {
      default_step_threads = static_cast<size_t>(std::stoul(argv[++i]));
    } else if (arg == "--respect-step-parallelism") {
      parse_optional_bool_arg(i, respect_step_parallelism);
    } else if (arg == "--no-respect-step-parallelism") {
      respect_step_parallelism = false;
    } else if (arg == "--deterministic") {
      parse_optional_bool_arg(i, deterministic);
    } else if (arg == "--interval" && i + 1 < argc) {
      interval = std::stod(argv[++i]);
    } else if (arg == "--iterations" && i + 1 < argc) {
      iterations = std::stoi(argv[++i]);
    } else if (arg == "--duration" && i + 1 < argc) {
      duration = std::stod(argv[++i]);
    } else if (arg == "--debug") {
      debug = true;
    }
  }

  const bool allow_direct = env_enabled(std::getenv("CF_ALLOW_DIRECT_ENGINE"));
  if (!allow_direct) {
    std::cerr << "Direct engine invocation is blocked by policy. "
              << "Use 'cf_ontology state activate -> state emit-event -> state run-loop'. "
              << "Set CF_ALLOW_DIRECT_ENGINE=1 for explicit override.\n";
    return 2;
  }

  if (pipeline_path.empty()) {
    print_usage();
    return 2;
  }

  cf::PipelineSpec pipeline;
  std::string error;
  if (!cf::load_pipeline_spec(pipeline_path, pipeline, error)) {
    std::cerr << error << "\n";
    return 1;
  }
  debug_check(debug, "Parse pipeline spec", pipeline_path);

  if (step_files.empty()) {
    step_files = pipeline.steps_files;
  }
  if (step_files.empty()) {
    std::cerr << "No steps catalogs provided. Pass --steps or add cf:hasStepsHeader to the pipeline.\n";
    return 2;
  }

  if (plugin_dirs.empty()) {
    plugin_dirs = pipeline.plugin_dirs;
  }
  if (plugin_dirs.empty()) {
    std::cerr << "No plugin directories provided. Pass --plugins or add cf:hasPluginsHeader to the pipeline.\n";
    return 2;
  }

  cf::StepCatalog catalog = cf::load_step_catalog(step_files, error);
  if (!error.empty()) {
    std::cerr << error << "\n";
    return 1;
  }
  debug_check(
      debug,
      "Load steps catalog",
      "files=" + std::to_string(step_files.size()) + ", steps=" + std::to_string(catalog.steps.size()));

  cf::PluginRegistry plugins;
  if (!plugins.load_from_directories(plugin_dirs, error, plugins_recursive)) {
    std::cerr << error << "\n";
    return 1;
  }
  debug_check(
      debug,
      "Load plugins",
      "dirs=" + std::to_string(plugin_dirs.size()) +
          ", libs=" + std::to_string(plugins.loaded_plugin_count()) +
          ", steps=" + std::to_string(plugins.loaded_step_count()));

  cf::ExecutionPlan plan;
  if (!cf::build_execution_plan(pipeline, catalog, plugins, plan, error)) {
    std::cerr << error << "\n";
    return 1;
  }
  debug_check(
      debug,
      "Build + validate execution plan",
      "nodes=" + std::to_string(plan.nodes.size()) +
          ", edges=" + std::to_string(plan.edges.size()) +
          ", levels=" + std::to_string(plan.levels.size()));

  if (debug) {
    std::cout << "[OK] Resolve step vtables\n";
    for (const auto& node : plan.nodes) {
      std::ostringstream line;
      line << "  - " << node.node_id << " :: " << node.step_iri;
      std::string plugin_path = plugins.plugin_path_for_step(node.step_iri);
      if (!plugin_path.empty()) {
        line << " | plugin=" << plugin_path;
      }
      if (node.vtable) {
        line << " | call=" << reinterpret_cast<const void*>(node.vtable->call);
        line << " | validate=" << reinterpret_cast<const void*>(node.vtable->validate);
        if (node.vtable->signature_hash) {
          line << " | sig=" << node.vtable->signature_hash;
        }
      } else {
        line << " | vtable=null";
      }
      std::cout << line.str() << "\n";
    }
  }

  if (duration > 0.0 && interval <= 0.0) {
    std::cerr << "Duration requires --interval > 0.\n";
    return 2;
  }
  if (iterations < 1) {
    iterations = 1;
  }
  if (duration > 0.0) {
    iterations = static_cast<int>(std::ceil(duration / interval));
  }

  cf::ConcurrencyOptions exec_options;
  exec_options.engine_threads = engine_threads;
  exec_options.max_total_threads = max_total_threads;
  exec_options.default_step_threads = default_step_threads;
  exec_options.respect_step_parallelism = respect_step_parallelism;
  exec_options.deterministic = deterministic;

  const auto resolved =
    cf::ConcurrencyController::resolve(exec_options, std::thread::hardware_concurrency());

  for (int i = 0; i < iterations; ++i) {
    if (debug) {
      std::cout << "[OK] Execute plan iteration " << (i + 1) << "/" << iterations
                << " (engine_threads=" << resolved.engine_threads
                << ", max_total_threads=" << resolved.max_total_threads << ")\n";
    }
    cf::ExecutionResult result = cf::execute_plan(plan, exec_options);
    if (!result.ok) {
      std::cerr << result.error << "\n";
      return 1;
    }
    if (interval > 0.0 && i + 1 < iterations) {
      std::this_thread::sleep_for(std::chrono::duration<double>(interval));
    }
  }

  return 0;
}
