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

#pragma once

#include <string>
#include <cstdint>

namespace dragonx {
namespace util {

/**
 * @brief Platform-specific utilities
 */
class Platform {
public:
    /**
     * @brief Open a URL in the default browser
     * @param url The URL to open
     * @return true if successful
     */
    static bool openUrl(const std::string& url);

    /**
     * @brief Open a folder in the system file manager
     * @param path Path to the folder
     * @param createIfMissing Create the folder if it doesn't exist
     * @return true if successful
     */
    static bool openFolder(const std::string& path, bool createIfMissing = true);

    /**
     * @brief Get the size of a file in bytes
     * @param path Path to the file
     * @return File size in bytes, or 0 if file doesn't exist
     */
    static uint64_t getFileSize(const std::string& path);

    /**
     * @brief Format a file size as human-readable string
     * @param bytes Size in bytes
     * @return Formatted string (e.g., "5.23 MB")
     */
    static std::string formatFileSize(uint64_t bytes);

    /**
     * @brief Get the user's home directory
     * @return Home directory path
     */
    static std::string getHomeDir();

    /**
     * @brief Get the DragonX data directory
     * @return Path like ~/.hush/DRAGONX/ or %APPDATA%\Hush\DRAGONX\
     */
    static std::string getDragonXDataDir();
    
    /**
     * @brief Get the wallet data directory (alias for getDragonXDataDir)
     * @return Path like ~/.hush/DRAGONX/ or %APPDATA%\Hush\DRAGONX\
     */
    static std::string getDataDir();

    /**
     * @brief Get the LITE wallet data directory (where the SilentDragonXLite backend stores
     *        silentdragonxlite-wallet.dat). Mirrors the backend's get_zcash_data_path() for the
     *        "main" chain: dirs::data_dir()/silentdragonxlite on Windows/macOS, ~/.silentdragonxlite
     *        on Linux. Distinct from getDragonXDataDir() (the full-node blocks/wallet dir).
     * @return Path like ~/.silentdragonxlite/ or %APPDATA%\silentdragonxlite\
     */
    static std::string getLiteWalletDataDir();
    
    /**
     * @brief Get the config directory for storing wallet exports/backups
     * @return Path like ~/.config/ObsidianDragon/ or %APPDATA%\ObsidianDragon\
     */
    static std::string getConfigDir();

    /**
     * @brief Delete a file
     * @param path Path to the file
     * @return true if successful or file didn't exist
     */
    static bool deleteFile(const std::string& path);

    /**
     * @brief Write a file atomically and durably.
     *
     * Writes @p content to `<path>.tmp`, flushes it to stable storage
     * (fsync / FlushFileBuffers), then atomically renames it over @p path
     * (POSIX rename / Win32 MoveFileEx with REPLACE_EXISTING). A crash or power
     * loss at any point leaves either the old file intact or the fully-written
     * new one — never a truncated/corrupt file. The parent directory is created
     * if missing, and on POSIX it is fsync'd so the rename itself is durable.
     *
     * @param path Destination file path.
     * @param content Bytes to write.
     * @param restrictPermissions When true, create the file owner-only (0600 on
     *        POSIX) before the rename so it is never briefly world-readable.
     * @return true on success; on failure any pre-existing file is left untouched.
     */
    static bool writeFileAtomically(const std::string& path,
                                    const std::string& content,
                                    bool restrictPermissions = false);
    
    /**
     * @brief Get the directory containing the executable
     * @return Path to executable's directory
     */
    static std::string getExecutableDirectory();

    /**
     * @brief Get the ObsidianDragon config directory
     * @return Path like ~/.config/ObsidianDragon/ or %APPDATA%\ObsidianDragon\
     */
    static std::string getObsidianDragonDir();

    /**
     * @brief Create ObsidianDragon folder structure and template files on first run
     *
     * Creates:
     *   ObsidianDragon/
     *   ObsidianDragon/themes/
     *   ObsidianDragon/themes/my_theme.toml  (template)
     *
     * Only writes template files if they don't already exist.
     */
    static void ensureObsidianDragonSetup();

    /**
     * @brief Create a directory (and parents) if missing, with a clear error on failure.
     *
     * Uses the non-throwing std::error_code overload internally. On failure sets *outError
     * (when non-null) to one consistent, user-facing message:
     *   "Cannot create <dir>: <reason>. Check permissions / free space."
     *
     * @return true if the directory exists (already did, or was just created).
     */
    static bool ensureDirectory(const std::string& dir, std::string* outError = nullptr);

    /**
     * @brief Get total system RAM in megabytes
     * @return Total physical RAM in MB, or 0 on failure
     */
    static double getTotalSystemRAM_MB();

    /**
     * @brief Get currently used system RAM in megabytes
     * @return Used physical RAM in MB (total - available), or 0 on failure
     */
    static double getUsedSystemRAM_MB();

    /**
     * @brief Get this process's own RSS (resident set size) in megabytes
     * @return Self process RSS in MB, or 0 on failure
     */
    static double getSelfMemoryUsageMB();

    /**
     * @brief Get total RSS of all dragonxd daemon processes in megabytes
     * Scans for any running dragonxd process by name, regardless of how it was launched.
     * @return Combined daemon RSS in MB, or 0 if no daemon found
     */
    static double getDaemonMemoryUsageMB();

    /**
     * @brief Get system-wide idle time in seconds
     * Uses platform-specific APIs: GetLastInputInfo (Windows),
     * XScreenSaverQueryInfo via dlopen (Linux), IOKit (macOS).
     * @return Seconds since last user input, or 0 on failure
     */
    static int getSystemIdleSeconds();

    /**
     * @brief Get GPU utilization percentage (0–100).
     * Linux: reads sysfs for AMD, /proc for NVIDIA.
     * Windows: queries PDH GPU engine counters.
     * @return GPU busy percent, or -1 if unavailable.
     */
    static int getGpuUtilization();

    // Run a command line and capture its stdout WITHOUT ever popping a console window: Windows uses
    // CreateProcess + CREATE_NO_WINDOW (a plain popen()/_popen() flashes a cmd.exe console), POSIX uses
    // popen(). Use this instead of _popen for anything run while the GUI is up. `mergeStderr` folds the
    // child's stderr into the result (like "2>&1"); otherwise stderr is discarded. `exitCode`, if given,
    // receives the child's exit status (raw pclose() status on POSIX, GetExitCodeProcess on Windows; -1
    // if the process could not be launched).
    static std::string runHiddenCapture(const std::string& cmdLine, bool mergeStderr = false,
                                        int* exitCode = nullptr);
};

/**
 * @brief Get the directory containing the executable (free function)
 * @return Path to executable's directory
 */
std::string getExecutableDirectory();

} // namespace util
} // namespace dragonx
