#pragma once

#include <nlohmann/json.hpp>

#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

namespace lm {

class IoModuleStore;

class IoModuleService
{
public:
  explicit IoModuleService(IoModuleStore& store);

  bool testConnection(const std::string& ip_address, int port, std::string& err) const;
  std::optional<nlohmann::json> resolveModule(const std::string& ref) const;

  bool connect(const std::string& module_id, std::string& err);
  bool disconnect(const std::string& module_id, std::string& err);
  bool isConnected(const std::string& module_id) const;

  bool setOutput(const std::string& module_id, int port, bool on, std::string& err);
  bool setOutputTimed(const std::string& module_id,
                      int port,
                      bool on,
                      int timeout_ms,
                      std::string& err);
  bool getInput(const std::string& module_id, int port, bool& value, std::string& err) const;
  bool waitInput(const std::string& module_id, int port, bool expected, int timeout_ms, std::string& err);

  bool applyZoneSettings(const nlohmann::json& zone, std::string& err);

  nlohmann::json runtimeSnapshot(const std::string& module_id) const;

  static bool validPortForType(const std::string& module_type, int port);
  static int defaultPortForType(const std::string& module_type);

private:
  IoModuleStore& store_;
  mutable std::mutex mu_;
  std::unordered_map<std::string, bool> connected_;
  std::unordered_map<std::string, std::vector<bool>> outputs_;
  std::unordered_map<std::string, std::vector<bool>> inputs_;

  void ensureSimStateUnlocked(const std::string& module_id, const std::string& module_type);
  bool requireConnectedUnlocked(const std::string& module_id, std::string& err) const;
};

}  // namespace lm
