//        Copyright Maarten L. Hekkelman, 2014-2026
//  Distributed under the Boost Software License, Version 1.0.
//     (See accompanying file LICENSE_1_0.txt or copy at
//           http://www.boost.org/LICENSE_1_0.txt)

#pragma once

/// \file
///
/// Source code specifically for Unix/Linux.
/// Utility routines to build daemon processes

#include "zeep/config.hpp"

#include <cstdint>
#include <functional>
#include <string>
#include <string_view>

namespace zeep::http
{

class basic_server;

/// \brief A class to create daemon processes easily
///
/// In UNIX a daemon is a process that runs in the background.
/// In the case of libzeep this is of course serving HTTP requests.
/// stderr and stdout are captured and written to the log files
/// specified and a process ID is store in the pid file which
/// allows checking the status of a running daemon.

class daemon
{
  public:
	/// \brief The factory for creating server instances.
	using server_factory_type = std::function<basic_server *()>;

	/// \brief constructor with separately specified files
	///
	/// \param factory			The function object that creates server instances
	/// \param pid_file			The file that will contain the process ID, usually in /var/run/{process_name}
	/// \param stdout_log_file	The file that will contain the stdout log, usually in /var/log/{process_name}/access.log
	/// \param stderr_log_file	The file that will contain the stderr log, usually in /var/log/{process_name}/error.log
	daemon(server_factory_type &&factory, std::string pid_file,
		std::string stdout_log_file, std::string stderr_log_file);

	/// \brief constructor with default files
	///
	/// \param factory			The function object that creates server instances
	/// \param name				The _process name_ to use, will be used to form default file locations
	daemon(server_factory_type &&factory, const std::string &name);

	/// \brief Avoid excessive automatic restart due to failing to start up
	///
	/// \param nr_of_restarts			The max number of attempts to take to start up a daemon process
	/// \param within_nr_of_seconds		The restart counter will only consider a failed restart if it fails
	///                                 starting up within this period of time.
	void set_max_restarts(int nr_of_restarts, int within_nr_of_seconds)
	{
		m_max_restarts = nr_of_restarts;
		m_restart_time_window = within_nr_of_seconds;
	}

#if HTTP_HAS_UNIX_DAEMON

	/// \brief Start the daemon, forking off in the background with multiple preforked servers
	///
	/// \param address				The address to bind to
	/// \param port					The port number to bind to
	/// \param nr_of_procs			The number of worker processes to fork
	/// \param nr_of_threads		The number of threads to pass to the server class
	/// \param run_as_user			The user to run the forked process. Daemons are usually
	///								started as root and should drop their privileges as soon
	///								as possible.
	int start(std::string_view address, uint16_t port, int nr_of_procs,
		int nr_of_threads, const std::string &run_as_user);

	/// \brief Start the daemon, forking off in the background with single process
	///
	/// \param address				The address to bind to
	/// \param port					The port number to bind to
	/// \param nr_of_threads		The number of threads to pass to the server class
	/// \param run_as_user			The user to run the forked process. Daemons are usually
	///								started as root and should drop their privileges as soon
	///								as possible.
	int start(std::string_view address, uint16_t port, int nr_of_threads,
		const std::string &run_as_user);

	/// \brief Stop a running daemon process. Returns 0 in case of successfully stopping a process.
	int stop();

	/// \brief Returns 0 if the daemon is running
	int status();

	/// \brief Force the running daemon to restart
	int reload();

#endif

	/// \brief Run the server without forking to the background
	///
	/// For debugging purposes it is sometimes useful to start a server
	/// without forking so you can see the stdout and stderr. Often this
	/// is done by adding a --no-daemon flag to the program options.
	int run_foreground(std::string_view address, uint16_t port);

  private:
#if HTTP_HAS_UNIX_DAEMON

	int daemonize();
	void open_log_file();

	bool run_main_loop(std::string_view address, uint16_t port, int nr_of_procs,
		int nr_of_threads, const std::string &run_as_user);

	bool pid_is_for_executable();
#endif

  private:
	server_factory_type m_factory;
	const std::string m_pid_file, m_stdout_log_file, m_stderr_log_file;

	int m_max_restarts = 5, m_restart_time_window = 10;
};

} // namespace zeep::http
