// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "single_instance.h"

#include <cstdio>

#include "../util/logger.h"

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/file.h>
#include <fcntl.h>
#include <errno.h>
#include <cstring>
#endif

namespace dragonx {
namespace util {

SingleInstance::SingleInstance(const std::string& app_name)
    : app_name_(app_name)
{
#ifndef _WIN32
    // Construct lock file path
    const char* tmpdir = getenv("TMPDIR");
    if (!tmpdir) tmpdir = "/tmp";
    lock_path_ = std::string(tmpdir) + "/" + app_name_ + ".lock";
#endif
}

SingleInstance::~SingleInstance()
{
    unlock();
}

bool SingleInstance::tryLock()
{
    if (locked_) {
        return true;  // Already locked by us
    }

#ifdef _WIN32
    // Windows: Use named mutex
    std::string mutex_name = "Global\\" + app_name_;
    
    mutex_handle_ = CreateMutexA(NULL, TRUE, mutex_name.c_str());
    
    if (mutex_handle_ == NULL) {
        DEBUG_LOGF("Failed to create mutex: %lu\n", GetLastError());
        return false;
    }
    
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        // Another instance is running
        CloseHandle(mutex_handle_);
        mutex_handle_ = nullptr;
        DEBUG_LOGF("Another instance of %s is already running (mutex exists)\n", app_name_.c_str());
        return false;
    }
    
    locked_ = true;
    DEBUG_LOGF("Single instance lock acquired (mutex)\n");
    return true;

#else
    // Linux/macOS: Use file lock (flock)
    lock_fd_ = open(lock_path_.c_str(), O_CREAT | O_RDWR, 0666);
    
    if (lock_fd_ < 0) {
        DEBUG_LOGF("Failed to open lock file %s: %s\n", lock_path_.c_str(), strerror(errno));
        return false;
    }
    
    // Try non-blocking exclusive lock
    if (flock(lock_fd_, LOCK_EX | LOCK_NB) != 0) {
        if (errno == EWOULDBLOCK) {
            // Another instance has the lock
            DEBUG_LOGF("Another instance of %s is already running (lock file: %s)\n", 
                   app_name_.c_str(), lock_path_.c_str());
        } else {
            DEBUG_LOGF("Failed to acquire lock: %s\n", strerror(errno));
        }
        close(lock_fd_);
        lock_fd_ = -1;
        return false;
    }
    
    // Write PID to lock file (useful for debugging)
    char pid_str[32];
    int len = snprintf(pid_str, sizeof(pid_str), "%d\n", getpid());
    if (ftruncate(lock_fd_, 0) == 0) {
        lseek(lock_fd_, 0, SEEK_SET);
        ssize_t written = write(lock_fd_, pid_str, len);
        (void)written;  // Ignore result, not critical
    }
    
    locked_ = true;
    DEBUG_LOGF("Single instance lock acquired (lock file: %s)\n", lock_path_.c_str());
    return true;
#endif
}

void SingleInstance::unlock()
{
    if (!locked_) {
        return;
    }

#ifdef _WIN32
    if (mutex_handle_ != nullptr) {
        ReleaseMutex(mutex_handle_);
        CloseHandle(mutex_handle_);
        mutex_handle_ = nullptr;
        DEBUG_LOGF("Single instance lock released (mutex)\n");
    }
#else
    if (lock_fd_ >= 0) {
        flock(lock_fd_, LOCK_UN);
        close(lock_fd_);
        lock_fd_ = -1;
        
        // Remove the lock file
        unlink(lock_path_.c_str());
        DEBUG_LOGF("Single instance lock released (lock file removed)\n");
    }
#endif
    
    locked_ = false;
}

} // namespace util
} // namespace dragonx
