#include "app/lidar_manager_app.hpp"

#include "auth/auth_service.hpp"
#include "io/io_module_service.hpp"
#include "io/io_zone_runtime.hpp"
#include "path/path_service.hpp"
#include "storage/path_store.hpp"
#include "storage/path_guide_store.hpp"
#include "mission/mission_enqueue.hpp"
#include "mission/mission_queue.hpp"
#include "mission/mission_scheduler.hpp"
#include "mission/mission_store.hpp"
#include "mission/modbus_trigger_service.hpp"
#include "robot/robot_runtime.hpp"
#include "server/api_server.hpp"
#include "server/static_file_server.hpp"
#include "storage/dashboard_store.hpp"
#include "storage/database.hpp"
#include "storage/map_store.hpp"
#include "storage/site_store.hpp"
#include "storage/sound_store.hpp"
#include "storage/io_module_store.hpp"
#include "storage/transition_store.hpp"
#include "storage/state_repository.hpp"

#include <httplib.h>
#include <cstdio>

namespace lm {

namespace {

bool isProjectRootDataPath(const std::filesystem::path& parent)
{
  return parent.empty() || parent == std::filesystem::path(".");
}

std::filesystem::path resolveDataPath(std::filesystem::path data_path)
{
  if (!isProjectRootDataPath(data_path.parent_path()))
    return data_path;
  if (data_path.filename() == "state.json")
    return std::filesystem::path("data") / "state.json";
  return std::filesystem::path("data") / "RBS.db";
}

}  // namespace

LidarManagerApp::LidarManagerApp(int port,
                                 std::filesystem::path www_root,
                                 std::filesystem::path data_path)
    : port_(port), www_root_(std::move(www_root)), data_path_(std::move(data_path))
{
}

int LidarManagerApp::run()
{
  data_path_ = resolveDataPath(data_path_);
  const std::filesystem::path data_dir = data_path_.parent_path();
  Database database(data_dir);
  std::string db_err;
  if (!database.init(db_err))
  {
    std::fprintf(stderr, "database init failed: %s\n", db_err.c_str());
    return 1;
  }

  StateRepository repo(data_path_, database);
  repo.load();

  MapStore map_store(database);
  TransitionStore transition_store(database);
  IoModuleStore io_module_store(database);
  IoModuleService io_module_service(io_module_store);
  IoZoneRuntime io_zone_runtime;
  PathStore path_store(database);
  PathGuideStore path_guide_store(database);
  PathService path_service(path_store, map_store);
  MissionStore mission_store(database);
  MissionQueue mission_queue(database, map_store, transition_store, mission_store, io_module_service, io_zone_runtime,
                               path_service);
  RobotRuntime robot_runtime(database, mission_queue);
  SiteStore site_store(database);
  site_store.ensureDefaultSiteId();
  SoundStore sound_store(database);
  {
    std::string sound_err;
    if (!sound_store.ensureSystemDefaults(sound_err))
      std::fprintf(stderr, "Warning: sound defaults: %s\n", sound_err.c_str());
  }
  DashboardStore dashboard_store(database);

  const auto enqueue_fn = [&mission_store, &mission_queue](const nlohmann::json& request, std::string& err) -> bool {
    nlohmann::json payload;
    if (!MissionEnqueue::buildPayload(mission_store, request, payload, err))
      return false;
    return static_cast<bool>(mission_queue.enqueue(payload, err));
  };

  ModbusTriggerService modbus(mission_store, enqueue_fn, 5502);
  MissionScheduler scheduler(mission_store, enqueue_fn);

  AuthService auth(database);

  httplib::Server svr;
  svr.set_pre_routing_handler([&auth](const httplib::Request& req, httplib::Response& res) {
    return auth.preRoute(req, res);
  });

  ApiServer api(repo,
                mission_queue,
                mission_store,
                modbus,
                scheduler,
                robot_runtime,
                map_store,
                site_store,
                sound_store,
                transition_store,
                io_module_store,
                io_module_service,
                path_store,
                path_guide_store,
                path_service,
                dashboard_store);
  api.registerRoutes(svr);
  auth.registerRoutes(svr);
  StaticFileServer::mount(svr, www_root_);

  std::fprintf(stderr,
               "lidar_manager_web listening on http://0.0.0.0:%d  (www=%s, db=%s, maps=%s, sounds=%s)\n",
               port_,
               www_root_.string().c_str(),
               database.dbPath().string().c_str(),
               database.mapsDir().string().c_str(),
               database.soundsDir().string().c_str());
  std::fprintf(stderr, "MiR REST API: http://0.0.0.0:%d/api/v2.0.0/mission_queue\n", port_);
  std::fprintf(stderr, "Modbus TCP triggers: port 5502 (coils 1001-2000)\n");

  svr.listen("0.0.0.0", port_);
  return 0;
}

}  // namespace lm
