#include "resolver.h"
#include "parser.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>

namespace nix_irc {

namespace fs = std::filesystem;

struct Resolver::Impl {
  ResolverConfig config;
  std::vector<std::pair<std::string, std::string>> resolved_imports;
  std::unordered_set<std::string> visited;
  Parser parser;

  Impl(const ResolverConfig& cfg) : config(cfg) {}

  std::string resolve_path(const std::string& path, const std::string& from_file) {
    fs::path p(path);

    if (p.is_absolute()) {
      if (fs::exists(p))
        return path;
      return "";
    }

    fs::path from_dir = fs::path(from_file).parent_path();
    fs::path candidate = from_dir / p;
    if (fs::exists(candidate))
      return candidate.string();

    for (const auto& search : config.search_paths) {
      candidate = fs::path(search) / p;
      if (fs::exists(candidate))
        return candidate.string();
    }

    return "";
  }

  ImportResult do_resolve(const std::string& path, const std::string& from_file) {
    std::string resolved = resolve_path(path, from_file);

    if (resolved.empty()) {
      return {false, "", "Cannot find file: " + path, nullptr};
    }

    if (visited.count(resolved)) {
      return {true, resolved, "", nullptr};
    }
    visited.insert(resolved);

    try {
      auto ast = parser.parse_file(resolved);
      return {true, resolved, "", ast};
    } catch (const std::exception& e) {
      return {false, "", e.what(), nullptr};
    }
  }
};

Resolver::Resolver(const ResolverConfig& config) : pImpl(std::make_unique<Impl>(config)) {}
Resolver::~Resolver() = default;

void Resolver::add_search_path(const std::string& path) {
  pImpl->config.search_paths.push_back(path);
}

void Resolver::set_search_paths(const std::vector<std::string>& paths) {
  pImpl->config.search_paths = paths;
}

ImportResult Resolver::resolve_import(const std::string& path, const std::string& from_file) {
  auto result = pImpl->do_resolve(path, from_file);
  if (result.success && result.ast) {
    pImpl->resolved_imports.push_back({path, result.path});
  }
  return result;
}

ImportResult Resolver::resolve_import(const Node& import_node, const std::string& from_file) {
  const ConstPathNode* path_node = import_node.get_if<ConstPathNode>();
  if (!path_node) {
    return {false, "", "Dynamic import not supported", nullptr};
  }
  return resolve_import(path_node->value, from_file);
}

std::vector<std::string> Resolver::get_resolved_files() const {
  std::vector<std::string> files;
  for (const auto& [orig, resolved] : pImpl->resolved_imports) {
    (void) orig;
    files.push_back(resolved);
  }
  return files;
}

std::vector<std::pair<std::string, std::string>> Resolver::get_imports() const {
  return pImpl->resolved_imports;
}

bool is_static_import(const Node& node) {
  return node.holds<ConstPathNode>();
}

std::string normalize_path(const std::string& path) {
  fs::path p(path);
  return fs::absolute(p).string();
}

} // namespace nix_irc
