#pragma once

#include <atomic>
#include <memory>
#include <span>
#include <thread>
#include <vector>

#include "config.hpp"
#include "connection_handler.hpp"
#include "connection_registry.hpp"

/**
 * High-performance multi-threaded server for handling network connections.
 *
 * The Server class encapsulates all networking logic including:
 * - Socket management and configuration
 * - Multi-threaded epoll-based I/O multiplexing
 * - Connection lifecycle management
 * - Graceful shutdown handling
 *
 * The server uses a configurable thread pool architecture:
 * - Accept threads: Handle incoming connections with load balancing
 * - Network threads: Process I/O events for established connections
 *
 * All protocol-specific logic is delegated to the provided ConnectionHandler,
 * maintaining clean separation between networking and application logic.
 *
 * IMPORTANT: Server uses a factory pattern and MUST be created via
 * Server::create(). This ensures:
 * - Proper shared_ptr semantics for enable_shared_from_this
 * - Safe weak_ptr references from Connection objects
 * - Prevention of accidental stack allocation that would break safety
 * guarantees
 */
struct Server : std::enable_shared_from_this<Server> {
  /**
   * Factory method to create a Server instance.
   *
   * This is the only way to create a Server - ensures proper shared_ptr
   * semantics and prevents accidental stack allocation that would break
   * weak_ptr safety.
   *
   * @param config Server configuration (threads, ports, limits, etc.)
   * @param handler Protocol handler for processing connection data
   * @param listen_fds Vector of file descriptors to accept connections on.
   *                   Server takes ownership and will close them on
   * destruction. Server will set these to non-blocking mode for safe epoll
   * usage. Empty vector means no listening sockets.
   * @return shared_ptr to the newly created Server
   */
  static std::shared_ptr<Server> create(const weaseldb::Config &config,
                                        ConnectionHandler &handler,
                                        const std::vector<int> &listen_fds);

  /**
   * Destructor ensures proper cleanup of all resources.
   */
  ~Server();

  /**
   * Start the server and begin accepting connections.
   *
   * This method:
   * - Creates and configures the listen socket
   * - Starts all worker threads
   * - Blocks until shutdown() is called or an error occurs
   *
   * Aborts the process on socket creation or configuration errors
   */
  void run();

  /**
   * Initiate graceful server shutdown.
   *
   * This method is async-signal-safe and can be called from signal handlers.
   * It signals all threads to stop processing and begin cleanup.
   *
   * The run() method will return after all threads have completed shutdown.
   */
  void shutdown();

  /**
   * Creates a local connection using socketpair() for testing or local IPC.
   *
   * Creates a socketpair, registers one end as a Connection in the server,
   * and returns the other end to the caller for communication.
   *
   * The caller takes ownership of the returned file descriptor and must close
   * it.
   *
   * @return File descriptor for the client end of the socketpair, or -1 on
   * error
   */
  int create_local_connection();

  /**
   * Release a connection back to its server for continued processing.
   *
   * This static method safely returns ownership of a connection back to its
   * server. If the server has been destroyed, the connection will be safely
   * cleaned up.
   *
   * This method is thread-safe and can be called from any thread.
   *
   * @param connection unique_ptr to the connection being released back
   */
  static void release_back_to_server(std::unique_ptr<Connection> connection);

private:
  friend struct Connection;
  /**
   * Private constructor - use create() factory method instead.
   *
   * @param config Server configuration (threads, ports, limits, etc.)
   * @param handler Protocol handler for processing connection data
   * @param listen_fds Vector of file descriptors to accept connections on.
   *                   Server takes ownership and will close them on
   * destruction. Server will set these to non-blocking mode for safe epoll
   * usage.
   */
  explicit Server(const weaseldb::Config &config, ConnectionHandler &handler,
                  const std::vector<int> &listen_fds);

  const weaseldb::Config &config_;
  ConnectionHandler &handler_;

  // Connection registry
  ConnectionRegistry connection_registry_;

  // Connection management
  std::atomic<int64_t> connection_id_{0};
  std::atomic<int> active_connections_{0};

  // Round-robin counter for connection distribution
  std::atomic<size_t> connection_distribution_counter_{0};

  // Shutdown coordination
  int shutdown_pipe_[2] = {-1, -1};

  // Multiple epoll file descriptors to reduce contention
  std::vector<int> epoll_fds_;
  std::vector<int>
      listen_fds_; // FDs to accept connections on (Server owns these)

  // Private helper methods
  void setup_shutdown_pipe();
  void setup_signal_handling();
  void create_epoll_instances();
  void start_io_threads(std::vector<std::thread> &threads);

  // Helper to get epoll fd for a thread using round-robin
  int get_epoll_for_thread(int thread_id) const;

  // Helper for processing connection I/O
  void process_connection_reads(std::unique_ptr<Connection> &conn_ptr,
                                int events);
  void process_connection_writes(std::unique_ptr<Connection> &conn_ptr,
                                 int events);

  // Helper for processing a batch of connections with their events
  void process_connection_batch(int epollfd,
                                std::span<std::unique_ptr<Connection>> batch,
                                std::span<const int> events);

  /**
   * Called internally to return ownership to the server.
   *
   * This method is thread-safe and can be called from any thread.
   * The connection will be re-added to the epoll for continued processing.
   *
   * @param connection Unique pointer to the connection being released back
   */
  void receiveConnectionBack(std::unique_ptr<Connection> connection);

  // Make non-copyable and non-movable
  Server(const Server &) = delete;
  Server &operator=(const Server &) = delete;
  Server(Server &&) = delete;
  Server &operator=(Server &&) = delete;
};
