#ifdef unix
#undef unix
#endif

#include "nix/expr/eval.hh"
#include "nix/expr/primops.hh"
#include "nix/expr/value.hh"
#include "nix/store/store-api.hh"
#include "nix/util/source-path.hh"

#include "irc/ir_gen.h"
#include "irc/parser.h"
#include "irc/resolver.h"
#include "irc/serializer.h"
#include "irc/types.h"

#include <fstream>
#include <iostream>
#include <memory>
#include <optional>

namespace nix_ir_plugin {

using namespace nix;
using namespace nix_irc;

/**
 * Load and evaluate a pre-compiled IR bundle
 * Usage: builtins.nixIR.loadIR "/path/to/file.nixir"
 */
static void prim_loadIR(EvalState &state, const PosIdx pos, Value **args,
                        Value &v) {
  auto path = state.forceStringNoCtx(
      *args[0], pos,
      "while evaluating the first argument to builtins.nixIR.loadIR");

  std::string pathStr(path);

  // Try to load the IR bundle
  std::ifstream file(pathStr, std::ios::binary | std::ios::ate);
  if (!file.is_open()) {
    state.error<EvalError>("cannot open IR bundle: %s", pathStr)
        .atPos(pos)
        .debugThrow();
  }

  // Read magic number to verify it's an IR file
  file.seekg(0);
  uint32_t magic = 0;
  file.read(reinterpret_cast<char *>(&magic), sizeof(magic));

  if (magic != IR_MAGIC) {
    state
        .error<EvalError>("not a valid IR bundle: %s (bad magic number)",
                          pathStr)
        .atPos(pos)
        .debugThrow();
  }

  // For now, just return a marker that this is an IR file
  // FIXME: complete full VM implementation
  auto bindings = state.buildBindings(2);

  Value *vType = state.allocValue();
  vType->mkString("ir-bundle");
  bindings.insert(state.symbols.create("type"), vType);

  Value *vPath = state.allocValue();
  vPath->mkString(pathStr);
  bindings.insert(state.symbols.create("path"), vPath);

  v.mkAttrs(bindings.finish());
}

/**
 * Compile Nix source to IR on-the-fly
 * Usage: builtins.nixIR.compile "{ x = 1; }"
 */
static void prim_compileNix(EvalState &state, const PosIdx pos, Value **args,
                            Value &v) {
  auto source = state.forceStringNoCtx(
      *args[0], pos,
      "while evaluating the first argument to builtins.nixIR.compile");

  std::string sourceStr(source);

  try {
    // Parse the Nix source
    Parser parser;
    auto ast = parser.parse(sourceStr, "<inline>");

    if (!ast) {
      state.error<EvalError>("failed to parse Nix expression")
          .atPos(pos)
          .debugThrow();
    }

    // Generate IR
    IRGenerator ir_gen;
    auto ir = ir_gen.generate(ast);

    // For now, return a marker that compilation succeeded
    // FIXME: full evaluation
    auto bindings = state.buildBindings(2);

    Value *vType = state.allocValue();
    vType->mkString("ir-compiled");
    bindings.insert(state.symbols.create("type"), vType);

    Value *vSource = state.allocValue();
    vSource->mkString(sourceStr.substr(0, 50));
    bindings.insert(state.symbols.create("source"), vSource);

    v.mkAttrs(bindings.finish());

  } catch (const std::exception &e) {
    state.error<EvalError>("IR compilation failed: %s", e.what())
        .atPos(pos)
        .debugThrow();
  }
}

/**
 * Get information about the IR plugin
 * Usage: builtins.nixIR.info
 */
static void prim_info(EvalState &state, const PosIdx pos, Value **args,
                      Value &v) {
  auto bindings = state.buildBindings(4);

  Value *vName = state.allocValue();
  vName->mkString("nix-ir-plugin");
  bindings.insert(state.symbols.create("name"), vName);

  Value *vVersion = state.allocValue();
  vVersion->mkString("0.1.0-alpha");
  bindings.insert(state.symbols.create("version"), vVersion);

  Value *vPhase = state.allocValue();
  vPhase->mkString("phase-4");
  bindings.insert(state.symbols.create("phase"), vPhase);

  Value *vStatus = state.allocValue();
  vStatus->mkString("compiler-complete");
  bindings.insert(state.symbols.create("status"), vStatus);

  v.mkAttrs(bindings.finish());
}

// Register primops on plugin load
static RegisterPrimOp rp_loadIR({
    .name = "__nixIR_loadIR",
    .args = {"path"},
    .arity = 1,
    .doc = R"(
      Load a pre-compiled IR bundle from the given path.
      Returns an attrset with information about the loaded bundle.

    )",
    .fun = prim_loadIR,
});

static RegisterPrimOp rp_compileNix({
    .name = "__nixIR_compile",
    .args = {"source"},
    .arity = 1,
    .doc = R"(
      Compile Nix source code to IR on-the-fly.
      Returns an attrset with information about the compiled IR.

    )",
    .fun = prim_compileNix,
});

static RegisterPrimOp rp_info({
    .name = "__nixIR_info",
    .args = {},
    .arity = 0,
    .doc = R"(
      Get information about the IR plugin.
      Returns an attrset with plugin name, version, and status.
    )",
    .fun = prim_info,
});

} // namespace nix_ir_plugin

// Plugin initialization message
__attribute__((constructor)) static void init_plugin() {
  std::cerr << "nix-ir-plugin loaded" << std::endl;
}
