#include "server.hpp"
#include "config.hpp"
#include "logger.hpp"
#include <iostream>
#include <fstream>
#include <string>
#include <csignal>
#include <atomic>
#include <memory>
#include <thread>
#include <drogon/drogon.h>

#include "version.hpp"

namespace simple_object_storage {

std::atomic<bool> g_running{true};
std::unique_ptr<Server> g_server;
std::thread g_server_thread;

std::filesystem::path get_executable_path() {
    return std::filesystem::read_symlink("/proc/self/exe");
}

std::filesystem::path get_config_path() {
    const std::filesystem::path _home = std::getenv("HOME");
    const std::filesystem::path _exe = get_executable_path().parent_path();

    // check for config in the following paths:
    std::vector<std::filesystem::path> config_paths = {
        "/data/sos_config.json",
        _home / ".config/simple-object-server/sos_config.json",
        _home / ".config/simple-object-server/config.json",
        _home / ".config/simple_object_storage/sos_config.json",
        _exe / "sos_config.json", 
        "/testing/sos_config.json"
    };

    for (const auto& path : config_paths) {
        if (std::filesystem::exists(path)) {
            return path;
        }
    }
    LOG_ERROR("No config file found. Checked:");
    for (const auto& path : config_paths) {
        LOG_ERROR("  {}", path.string());
    }
    return std::filesystem::path();
}

bool initialize_server() {
    std::filesystem::path config_path = get_config_path();
    if (config_path.empty()) {
        return false;
    }
    
    ServerConfig config;
    if (!simple_object_storage::load_config(config_path, config)) {
        LOG_ERROR("Config file at {} is not valid.", config_path.string());
        return false;
    }
    
    // Reinitialize logger with config settings
    Logger::init(config.log_level, config.log_file_path.string());
    
    LOG_INFO("Config file: {}", config_path.string());
    LOG_INFO("Starting server at {}:{}", config.host, config.port);
    LOG_INFO("Object store path: {}", config.object_store_path.string());
    if (!config.log_file_path.empty()) {
        LOG_INFO("Log file: {}", config.log_file_path.string());
    }

    if (!std::filesystem::exists(config.object_store_path)) {
        LOG_ERROR("Object store path does not exist: {}", config.object_store_path.string());
        return false;
    }

    g_server = std::make_unique<Server>(config);
    return true;
}

void stop_server() {
    if (g_server) {
        LOG_INFO("Stopping server...");
        g_server->stop();
        if (g_server_thread.joinable()) {
            g_server_thread.join();
        }
        g_server.reset();
    }
}

void signal_handler(int signal) {
    switch (signal) {
        case SIGHUP:
            LOG_INFO("Received SIGHUP signal - reloading configuration");
            stop_server();
            if (!initialize_server()) {
                LOG_ERROR("Failed to restart server with new configuration");
                g_running = false;
            }
            break;
        case SIGTERM:
        case SIGINT:
            LOG_INFO("Received termination signal");
            g_running = false;
            break;
    }
}

int main(int argc, char* argv[]) {
    // Initialize logger with defaults (will be reconfigured after loading config)
    Logger::init("info", "");
    
    if (argc > 1) {
        if (std::string(argv[1]) == "version") {
            std::cout << dropshell::VERSION << std::endl;
            return 0;
        }
    }

    // Set up signal handlers
    struct sigaction sa;
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    if (sigaction(SIGHUP, &sa, nullptr) == -1) {
        LOG_ERROR("Failed to set up SIGHUP handler");
        return 1;
    }
    if (sigaction(SIGTERM, &sa, nullptr) == -1) {
        LOG_ERROR("Failed to set up SIGTERM handler");
        return 1;
    }
    if (sigaction(SIGINT, &sa, nullptr) == -1) {
        LOG_ERROR("Failed to set up SIGINT handler");
        return 1;
    }

    // Initial server startup
    if (!initialize_server()) {
        return 1;
    }
    
    LOG_INFO("Simple Object Storage version: {}", dropshell::VERSION);

    // Start server in main thread
    if (!g_server->start()) {
        LOG_ERROR("Failed to start server");
        return 1;
    }

    return 0;
} 

} // namespace simple_object_storage

int main(int argc, char* argv[]) {
    return simple_object_storage::main(argc, argv);
}