#pragma once

#include "embedded_daemon.h"

#include <algorithm>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>

namespace dragonx {
namespace config { class Settings; }
namespace daemon {

class DaemonController {
public:
    using State = EmbeddedDaemon::State;
    using StateCallback = EmbeddedDaemon::StateCallback;

    enum class ShutdownAction {
        DisconnectOnly,
        StopDaemon
    };

    struct ShutdownDecision {
        ShutdownAction action = ShutdownAction::DisconnectOnly;
        const char* logReason = "no embedded daemon";
        const char* status = "Disconnecting...";
    };

    enum class LifecycleOperation {
        ManualRestart,
        Rescan,
        RepairWallet,        // restart with -zapwallettxes=2 (wipe & rebuild wallet tx records)
        DeleteBlockchainData,
        BootstrapStop
    };

    struct LifecycleDecision {
        LifecycleOperation operation = LifecycleOperation::ManualRestart;
        bool allowed = false;
        bool wasRunning = false;
        const char* taskName = "";
        const char* status = "";
        const char* warning = "";
        bool resetCrashCount = false;
        bool setRescanOnNextStart = false;
        bool disconnectRpc = false;
        int restartDelayMs = 0;
        bool setZapOnNextStart = false;
    };

    class LifecycleTaskContext {
    public:
        virtual ~LifecycleTaskContext() = default;
        virtual bool cancelled() const = 0;
        virtual bool shuttingDown() const = 0;
        virtual void sleepForMs(int milliseconds) = 0;
    };

    class LifecycleRuntime {
    public:
        virtual ~LifecycleRuntime() = default;
        virtual void stopDaemonWithPolicy() = 0;
        virtual bool startDaemon() = 0;
        virtual int deleteBlockchainData() = 0;
        virtual void resetOutputOffset() = 0;
        virtual void requestRpcStopAndDisconnect(const char* context, const char* reason) = 0;
    };

    struct LifecycleExecutionResult {
        bool completed = false;
        bool cancelled = false;
        bool stopped = false;
        bool started = false;
        int deletedItems = 0;
    };

    DaemonController();
    ~DaemonController();

    DaemonController(const DaemonController&) = delete;
    DaemonController& operator=(const DaemonController&) = delete;

    EmbeddedDaemon* daemon() { return daemon_.get(); }
    const EmbeddedDaemon* daemon() const { return daemon_.get(); }

    void setStateCallback(StateCallback callback);
    void syncSettings(const config::Settings* settings);

    bool start(const config::Settings* settings);
    void stop(int waitMs);

    bool isRunning() const;
    bool externalDaemonDetected() const;
    void clearExternalDaemonDetected();
    State state() const;
    std::string lastError() const;   // by value: EmbeddedDaemon::getLastError() returns a locked copy (M-04)
    int crashCount() const;
    int lastBlockHeight() const;
    double memoryUsageMB() const;
    std::vector<std::string> recentLines(std::size_t count) const;
    std::string outputSince(std::size_t& offset) const;

    void resetCrashCount();
    void setRescanOnNextStart(bool enabled);
    bool rescanOnNextStart() const;
    void setZapOnNextStart(bool enabled);
    bool zapOnNextStart() const;
    void setSalvageOnNextStart(bool enabled);
    void setReindexOnNextStart(bool enabled);   // -reindex: rebuild the block DB from raw blocks on next start

    static ShutdownDecision evaluateShutdownPolicy(bool hasDaemon,
                                                   bool externalDaemonDetected,
                                                   bool keepDaemonRunning,
                                                   bool stopExternalDaemon) {
        if (!hasDaemon) {
            return {};
        }
        if (keepDaemonRunning) {
            return {ShutdownAction::DisconnectOnly,
                    "keep_daemon_running enabled",
                    "Disconnecting (daemon stays running)..."};
        }
        if (externalDaemonDetected && !stopExternalDaemon) {
            return {ShutdownAction::DisconnectOnly,
                    "external daemon (not ours to stop)",
                    "Disconnecting (daemon stays running)..."};
        }
        return {ShutdownAction::StopDaemon,
                "stopping managed daemon",
                "Sending stop command to daemon..."};
    }
    static LifecycleDecision evaluateLifecycleOperation(LifecycleOperation operation,
                                                        bool usingEmbeddedDaemon,
                                                        bool hasDaemon,
                                                        bool daemonRunning,
                                                        bool restartInProgress = false) {
        switch (operation) {
            case LifecycleOperation::ManualRestart:
                if (!usingEmbeddedDaemon || restartInProgress) return {};
                return {operation, true, daemonRunning, "daemon-restart", "Restarting daemon...", "",
                        true, false, true, 500};
            case LifecycleOperation::Rescan:
                if (!usingEmbeddedDaemon || !hasDaemon) {
                    return {operation, false, daemonRunning, "", "",
                            "Rescan requires embedded daemon. Restart your daemon with -rescan manually."};
                }
                return {operation, true, daemonRunning, "rescan-blockchain", "Starting rescan...", "",
                        false, true, false, 3000};
            case LifecycleOperation::RepairWallet:
                if (!usingEmbeddedDaemon || !hasDaemon) {
                    return {operation, false, daemonRunning, "", "",
                            "Wallet repair requires embedded daemon. Restart your daemon with -zapwallettxes=2 manually."};
                }
                return {operation, true, daemonRunning, "repair-wallet", "Repairing wallet...", "",
                        false, false, false, 3000, true};
            case LifecycleOperation::DeleteBlockchainData:
                if (!usingEmbeddedDaemon || !hasDaemon) {
                    return {operation, false, daemonRunning, "", "",
                            "Delete blockchain requires embedded daemon. Stop your daemon manually and delete the data directory."};
                }
                return {operation, true, daemonRunning, "delete-blockchain-data", "Deleting blockchain data...", "",
                        false, false, false, 3000};
            case LifecycleOperation::BootstrapStop:
                return {operation, true, daemonRunning, "bootstrap-stop-daemon", "Stopping daemon for bootstrap...", "",
                        false, false, true, 0};
        }
        return {};
    }
    void prepareLifecycleOperation(const LifecycleDecision& decision,
                                   const config::Settings* settings = nullptr);
    static inline LifecycleExecutionResult executeLifecycleOperation(const LifecycleDecision& decision,
                                                                    LifecycleRuntime& runtime,
                                                                    LifecycleTaskContext& task)
    {
        LifecycleExecutionResult result;
        if (!decision.allowed) return result;

        auto waitForDelay = [&]() {
            int waitTicks = std::max(0, decision.restartDelayMs / 100);
            for (int i = 0; i < waitTicks && !task.cancelled() && !task.shuttingDown(); ++i) {
                task.sleepForMs(100);
            }
        };

        auto cancelled = [&]() {
            result.cancelled = task.cancelled() || task.shuttingDown();
            return result.cancelled;
        };

        switch (decision.operation) {
            case LifecycleOperation::BootstrapStop:
                if (decision.wasRunning) {
                    runtime.requestRpcStopAndDisconnect("Bootstrap daemon stop", "Bootstrap");
                    result.stopped = true;
                }
                result.completed = true;
                return result;
            case LifecycleOperation::ManualRestart:
                if (decision.wasRunning) {
                    runtime.stopDaemonWithPolicy();
                    result.stopped = true;
                }
                break;
            case LifecycleOperation::Rescan:
            case LifecycleOperation::RepairWallet:
            case LifecycleOperation::DeleteBlockchainData:
                runtime.stopDaemonWithPolicy();
                result.stopped = true;
                break;
        }

        if (cancelled()) return result;
        waitForDelay();
        if (cancelled()) return result;

        if (decision.operation == LifecycleOperation::DeleteBlockchainData) {
            result.deletedItems = runtime.deleteBlockchainData();
            if (cancelled()) return result;
        }

        if (decision.operation == LifecycleOperation::Rescan ||
            decision.operation == LifecycleOperation::RepairWallet ||
            decision.operation == LifecycleOperation::DeleteBlockchainData) {
            runtime.resetOutputOffset();
        }

        result.started = runtime.startDaemon();
        result.completed = !cancelled();
        return result;
    }
    ShutdownDecision shutdownDecision(bool keepDaemonRunning,
                                      bool stopExternalDaemon) const;

private:
    std::unique_ptr<EmbeddedDaemon> daemon_;
};

} // namespace daemon
} // namespace dragonx
