#include "daemon_controller.h"
#include "../config/settings.h"
#include "../util/platform.h"

#include <algorithm>
#include <filesystem>
#include <system_error>

namespace dragonx {
namespace daemon {

DaemonController::DaemonController()
    : daemon_(std::make_unique<EmbeddedDaemon>())
{
}

DaemonController::~DaemonController() = default;

void DaemonController::setStateCallback(StateCallback callback)
{
    daemon_->setStateCallback(std::move(callback));
}

void DaemonController::syncSettings(const config::Settings* settings)
{
    if (!settings) return;
    daemon_->setDebugCategories(settings->getDebugCategories());
    daemon_->setMaxConnections(settings->getMaxConnections());
    daemon_->setStratumHosting(settings->getStratumHost(), settings->getStratumAllowIp());

    std::string walletFile = settings->getActiveWalletFile();
    // The Wallets dialog opens an out-of-datadir wallet by linking it into the datadir under a
    // "wallet-ip-<hash>.dat" name. If that link went dangling (the external file was moved / a USB was
    // unplugged between sessions), don't let the daemon create a fresh EMPTY wallet at that name — fall
    // back to the default this launch. fs::exists follows the link, so it's false for a dangling one.
    if (walletFile.rfind("wallet-ip-", 0) == 0) {
        std::error_code ec;
        if (!std::filesystem::exists(util::Platform::getDragonXDataDir() + "/" + walletFile, ec))
            walletFile = "wallet.dat";
    }
    daemon_->setWalletFile(walletFile);
}

bool DaemonController::start(const config::Settings* settings)
{
    syncSettings(settings);
    return daemon_->start();
}

void DaemonController::stop(int waitMs)
{
    daemon_->stop(waitMs);
}

bool DaemonController::isRunning() const
{
    return daemon_->isRunning();
}

bool DaemonController::externalDaemonDetected() const
{
    return daemon_->externalDaemonDetected();
}

void DaemonController::clearExternalDaemonDetected()
{
    daemon_->clearExternalDaemonDetected();
}

DaemonController::State DaemonController::state() const
{
    return daemon_->getState();
}

std::string DaemonController::lastError() const
{
    // By value — getLastError() now returns a mutex-locked COPY, so forwarding it by reference would
    // dangle (bind a reference to that temporary). (M-04 follow-through)
    return daemon_ ? daemon_->getLastError() : std::string();
}

int DaemonController::crashCount() const
{
    return daemon_->getCrashCount();
}

int DaemonController::lastBlockHeight() const
{
    return daemon_ ? daemon_->getLastBlockHeight() : 0;
}

double DaemonController::memoryUsageMB() const
{
    return daemon_ ? daemon_->getMemoryUsageMB() : 0.0;
}

std::vector<std::string> DaemonController::recentLines(std::size_t count) const
{
    return daemon_ ? daemon_->getRecentLines(count) : std::vector<std::string>{};
}

std::string DaemonController::outputSince(std::size_t& offset) const
{
    return daemon_ ? daemon_->getOutputSince(offset) : std::string{};
}

void DaemonController::resetCrashCount()
{
    daemon_->resetCrashCount();
}

void DaemonController::setRescanOnNextStart(bool enabled)
{
    daemon_->setRescanOnNextStart(enabled);
}

bool DaemonController::rescanOnNextStart() const
{
    return daemon_->rescanOnNextStart();
}

void DaemonController::setZapOnNextStart(bool enabled)
{
    daemon_->setZapOnNextStart(enabled);
}

void DaemonController::setSalvageOnNextStart(bool enabled)
{
    daemon_->setSalvageOnNextStart(enabled);
}

void DaemonController::setReindexOnNextStart(bool enabled)
{
    daemon_->setReindexOnNextStart(enabled);
}

bool DaemonController::zapOnNextStart() const
{
    return daemon_->zapOnNextStart();
}

void DaemonController::prepareLifecycleOperation(const LifecycleDecision& decision,
                                                 const config::Settings* settings)
{
    if (settings) syncSettings(settings);
    if (decision.resetCrashCount) resetCrashCount();
    if (decision.setRescanOnNextStart) setRescanOnNextStart(true);
    if (decision.setZapOnNextStart) setZapOnNextStart(true);
}

DaemonController::ShutdownDecision DaemonController::shutdownDecision(
    bool keepDaemonRunning, bool stopExternalDaemon) const
{
    return evaluateShutdownPolicy(static_cast<bool>(daemon_),
                                  daemon_ && daemon_->externalDaemonDetected(),
                                  keepDaemonRunning,
                                  stopExternalDaemon);
}

} // namespace daemon
} // namespace dragonx
