// ============================================================================
// wintool.cpp
// ----------------------------------------------------------------------------
// MSVC / Windows / C++20 常用 Win32 工具库实现文件。
//
// 本文件实现 wintool.h 中声明的全部函数。中文注释重点说明实现意图、
// 资源所有权和 Win32 API 使用注意事项；具体接口作用和用法请优先查看
// wintool.h 中每个类和函数前的注释。
// ============================================================================
#include "../include/wintool.h"

#include <shellapi.h>
#include <shlobj_core.h>
#include <algorithm>
#include <iomanip>
#include <limits>
#include <sstream>
#include <utility>

#if defined(_MSC_VER)
#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "Ole32.lib")
#pragma comment(lib, "Shell32.lib")
#pragma comment(lib, "User32.lib")
#endif

namespace {

// 匿名命名空间中的 z_ 前缀函数仅供本实现文件内部使用，避免污染外部符号。

// Windows 中有效 HANDLE 既不能是 nullptr，也不能是 INVALID_HANDLE_VALUE。
[[nodiscard]] bool z_is_valid_handle(HANDLE h) noexcept {
    return h != nullptr && h != INVALID_HANDLE_VALUE;
}

// 将 string_view 显式复制为以 NUL 结尾的 std::wstring，便于传给 Win32 API。
[[nodiscard]] std::wstring z_to_wstring(std::wstring_view value) {
    return std::wstring{value};
}

// 注册表默认值使用 nullptr 表示；普通命名值使用字符串指针。
[[nodiscard]] const wchar_t *z_registry_value_name_ptr(
    const std::wstring &valueName) noexcept {
    return valueName.empty() ? nullptr : valueName.c_str();
}

// 将注册表 API 的 LSTATUS 错误统一转换为 Win32Error。
void z_throw_lstatus(LSTATUS status, std::string_view context) {
    if (status != ERROR_SUCCESS) {
        throw ZWINTOOL::Win32Error(static_cast<DWORD>(status), context);
    }
}

// 生成十六进制错误码文本，便于调试日志中定位问题。
[[nodiscard]] std::wstring z_hex_w(DWORD value) {
    std::wostringstream os;
    os << L"0x" << std::uppercase << std::hex << std::setw(8) << std::setfill(L'0') <<
            value;
    return os.str();
}

// 生成 UTF-8/窄字符串形式的十六进制错误码文本。
[[nodiscard]] std::string z_hex_a(DWORD value) {
    std::ostringstream os;
    os << "0x" << std::uppercase << std::hex << std::setw(8) << std::setfill('0') <<
            value;
    return os.str();
}

// FormatMessage 返回文本通常带换行，这里统一去掉尾部空白字符。
[[nodiscard]] std::wstring z_trim_message(std::wstring message) {
    while (!message.empty() && (message.back() == L'\r' || message.back() == L'\n' ||
                                message.back() == L' ' || message.back() == L'\t')) {
        message.pop_back();
    }
    return message;
}

// 组合 Win32Error 的 what() 文本：上下文 + 系统消息 + 数值错误码。
[[nodiscard]] std::string z_make_win32_error_text(DWORD error, std::string_view context) {
    std::string text;
    if (!context.empty()) {
        text.append(context.data(), context.size());
        text.append(": ");
    }
    text += ZWINTOOL::ErrorMessageA(error);
    text += " [Win32 ";
    text += std::to_string(error);
    text += ", ";
    text += z_hex_a(error);
    text += "]";
    return text;
}

// 组合 HResultError 的 what() 文本：上下文 + 系统消息 + HRESULT。
[[nodiscard]] std::string
z_make_hresult_error_text(HRESULT hr, std::string_view context) {
    std::string text;
    if (!context.empty()) {
        text.append(context.data(), context.size());
        text.append(": ");
    }
    text += ZWINTOOL::HResultMessageA(hr);
    text += " [HRESULT ";
    text += z_hex_a(static_cast<DWORD>(hr));
    text += "]";
    return text;
}

// 确保 RegKey 对象持有有效 HKEY；空对象调用读写会抛逻辑错误。
void z_require_key(HKEY key) {
    if (!key) {
        throw std::logic_error("Z::RegKey is empty");
    }
}

// Win32 ReadFile / WriteFile 单次字节数使用 DWORD，超过时需要分块处理。
[[nodiscard]] DWORD z_span_chunk_size(size_t remaining) noexcept {
    constexpr size_t maxChunk = static_cast<size_t>((std::numeric_limits<DWORD>::max)());
    return static_cast<DWORD>((std::min)(remaining, maxChunk));
}

// 剪贴板打开后必须 CloseClipboard；用 RAII 避免异常路径泄漏状态。
class ClipboardGuard final {
public:
    explicit ClipboardGuard(HWND owner) {
        if (!::OpenClipboard(owner)) {
            ZWINTOOL::ThrowLastError("OpenClipboard");
        }
    }

    ~ClipboardGuard() noexcept {
        ::CloseClipboard();
    }

    ClipboardGuard(const ClipboardGuard &) = delete;
    ClipboardGuard &operator=(const ClipboardGuard &) = delete;
};

} // namespace

namespace ZWINTOOL {

// -----------------------------------------------------------------------------
// 错误处理实现
// -----------------------------------------------------------------------------

// 构造异常时立即生成可读文本，便于直接输出 e.what()。
Win32Error::Win32Error(DWORD error, std::string_view context)
    : std::runtime_error(z_make_win32_error_text(error, context)), code_(error) {}

DWORD Win32Error::code() const noexcept {
    return code_;
}

HResultError::HResultError(HRESULT hr, std::string_view context)
    : std::runtime_error(z_make_hresult_error_text(hr, context)), hr_(hr) {}

HRESULT HResultError::hr() const noexcept {
    return hr_;
}

DWORD LastError() noexcept {
    return ::GetLastError();
}

// 使用 FormatMessageW 从系统错误码获取本地化错误文本。
std::wstring ErrorMessageW(DWORD error) {
    if (error == ERROR_SUCCESS) {
        return L"The operation completed successfully.";
    }

    LPWSTR buffer = nullptr;
    const DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM |
                        FORMAT_MESSAGE_IGNORE_INSERTS;

    const DWORD length = ::FormatMessageW(
        flags,
        nullptr,
        error,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPWSTR>(&buffer),
        0,
        nullptr);

    if (length == 0 || !buffer) {
        return L"Windows error " + std::to_wstring(error) + L" (" + z_hex_w(error) + L")";
    }

    std::wstring message{buffer, length};
    ::LocalFree(buffer);
    return z_trim_message(std::move(message));
}

std::string ErrorMessageA(DWORD error) {
    return WideToUtf8(ErrorMessageW(error));
}

// HRESULT 优先按系统错误信息格式化；失败时退回十六进制显示。
std::wstring HResultMessageW(HRESULT hr) {
    LPWSTR buffer = nullptr;
    const DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM |
                        FORMAT_MESSAGE_IGNORE_INSERTS;

    const DWORD length = ::FormatMessageW(
        flags,
        nullptr,
        static_cast<DWORD>(hr),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        reinterpret_cast<LPWSTR>(&buffer),
        0,
        nullptr);

    if (length == 0 || !buffer) {
        return L"HRESULT " + z_hex_w(static_cast<DWORD>(hr));
    }

    std::wstring message{buffer, length};
    ::LocalFree(buffer);
    return z_trim_message(std::move(message));
}

std::string HResultMessageA(HRESULT hr) {
    return WideToUtf8(HResultMessageW(hr));
}

void ThrowLastError(std::string_view context) {
    throw Win32Error(::GetLastError(), context);
}

void ThrowIfFalse(BOOL ok, std::string_view context) {
    if (!ok) {
        ThrowLastError(context);
    }
}

void ThrowIfNull(const void *pointer, std::string_view context) {
    if (!pointer) {
        ThrowLastError(context);
    }
}

void ThrowIfInvalidHandle(HANDLE handle, std::string_view context) {
    if (!z_is_valid_handle(handle)) {
        ThrowLastError(context);
    }
}

void CheckHResult(HRESULT hr, std::string_view context) {
    if (FAILED(hr)) {
        throw HResultError(hr, context);
    }
}

// -----------------------------------------------------------------------------
// 字符串编码转换实现
// -----------------------------------------------------------------------------

// MultiByteToWideChar 分两步调用：先取长度，再分配缓冲区转换。
std::wstring MultiByteToWide(std::string_view text, UINT codePage) {
    if (text.empty()) {
        return {};
    }
    if (text.size() > static_cast<size_t>((std::numeric_limits<int>::max)())) {
        throw std::length_error("string too large for MultiByteToWideChar");
    }

    const DWORD flags = (codePage == CP_UTF8) ? MB_ERR_INVALID_CHARS : 0;
    const int inputLength = static_cast<int>(text.size());
    const int required = ::MultiByteToWideChar(codePage, flags, text.data(), inputLength,
                                               nullptr, 0);
    if (required == 0) {
        ThrowLastError("MultiByteToWideChar");
    }

    std::wstring result(static_cast<size_t>(required), L'\0');
    const int written = ::MultiByteToWideChar(codePage, flags, text.data(), inputLength,
                                              result.data(), required);
    if (written == 0) {
        ThrowLastError("MultiByteToWideChar");
    }
    result.resize(static_cast<size_t>(written));
    return result;
}

// WideCharToMultiByte 分两步调用：先取长度，再分配缓冲区转换。
std::string WideToMultiByte(std::wstring_view text, UINT codePage) {
    if (text.empty()) {
        return {};
    }
    if (text.size() > static_cast<size_t>((std::numeric_limits<int>::max)())) {
        throw std::length_error("string too large for WideCharToMultiByte");
    }

    const DWORD flags = (codePage == CP_UTF8) ? WC_ERR_INVALID_CHARS : 0;
    const int inputLength = static_cast<int>(text.size());
    const int required = ::WideCharToMultiByte(codePage, flags, text.data(), inputLength,
                                               nullptr, 0, nullptr, nullptr);
    if (required == 0) {
        ThrowLastError("WideCharToMultiByte");
    }

    std::string result(static_cast<size_t>(required), '\0');
    const int written = ::WideCharToMultiByte(codePage, flags, text.data(), inputLength,
                                              result.data(), required, nullptr, nullptr);
    if (written == 0) {
        ThrowLastError("WideCharToMultiByte");
    }
    result.resize(static_cast<size_t>(written));
    return result;
}

std::wstring Utf8ToWide(std::string_view text) {
    return MultiByteToWide(text, CP_UTF8);
}

std::string WideToUtf8(std::wstring_view text) {
    return WideToMultiByte(text, CP_UTF8);
}

// -----------------------------------------------------------------------------
// RAII Win32 资源封装实现
// -----------------------------------------------------------------------------

Handle::Handle(HANDLE handle) noexcept : handle_(handle) {}

// 析构自动释放 HANDLE，移动语义用于转移所有权。
Handle::~Handle() noexcept {
    reset();
}

Handle::Handle(Handle &&other) noexcept : handle_(other.release()) {}

Handle &Handle::operator=(Handle &&other) noexcept {
    if (this != &other) {
        reset(other.release());
    }
    return *this;
}

HANDLE Handle::get() const noexcept {
    return handle_;
}

HANDLE *Handle::put() noexcept {
    reset();
    return &handle_;
}

bool Handle::valid() const noexcept {
    return z_is_valid_handle(handle_);
}

Handle::operator bool() const noexcept {
    return valid();
}

HANDLE Handle::release() noexcept {
    HANDLE old = handle_;
    handle_ = nullptr;
    return old;
}

void Handle::reset(HANDLE handle) noexcept {
    if (z_is_valid_handle(handle_)) {
        ::CloseHandle(handle_);
    }
    handle_ = handle;
}

Library::Library(std::wstring_view path) {
    load(path);
}

// 析构自动释放 DLL 模块，移动语义用于转移所有权。
Library::~Library() noexcept {
    reset();
}

Library::Library(Library &&other) noexcept : module_(other.release()) {}

Library &Library::operator=(Library &&other) noexcept {
    if (this != &other) {
        reset(other.release());
    }
    return *this;
}

HMODULE Library::get() const noexcept {
    return module_;
}

bool Library::valid() const noexcept {
    return module_ != nullptr;
}

Library::operator bool() const noexcept {
    return valid();
}

void Library::load(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    HMODULE module = ::LoadLibraryW(p.c_str());
    if (!module) {
        ThrowLastError("LoadLibraryW");
    }
    reset(module);
}

HMODULE Library::release() noexcept {
    HMODULE old = module_;
    module_ = nullptr;
    return old;
}

void Library::reset(HMODULE module) noexcept {
    if (module_) {
        ::FreeLibrary(module_);
    }
    module_ = module;
}

// S_OK 和 S_FALSE 都表示当前线程可使用 COM；RPC_E_CHANGED_MODE 表示模式冲突。
ComApartment::ComApartment(DWORD coinit) {
    hr_ = ::CoInitializeEx(nullptr, coinit);
    if (SUCCEEDED(hr_)) {
        initialized_ = true;
    } else {
        throw HResultError(hr_, "CoInitializeEx");
    }
}

ComApartment::~ComApartment() noexcept {
    if (initialized_) {
        ::CoUninitialize();
    }
}

HRESULT ComApartment::hr() const noexcept {
    return hr_;
}

// -----------------------------------------------------------------------------
// 路径与环境变量实现
// -----------------------------------------------------------------------------

// GetModuleFileNameW 缓冲区不足时会截断，因此这里循环扩大缓冲区。
std::wstring ModulePathW(HMODULE module) {
    std::wstring buffer(MAX_PATH, L'\0');
    for (;;) {
        const DWORD length = ::GetModuleFileNameW(module, buffer.data(),
                                                  static_cast<DWORD>(buffer.size()));
        if (length == 0) {
            ThrowLastError("GetModuleFileNameW");
        }
        if (length < buffer.size()) {
            buffer.resize(length);
            return buffer;
        }
        buffer.resize(buffer.size() * 2);
    }
}

std::wstring ParentPathW(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    while (p.size() > 3 && (p.back() == L'\\' || p.back() == L'/')) {
        p.pop_back();
    }

    const size_t pos = p.find_last_of(L"\\/");
    if (pos == std::wstring::npos) {
        return {};
    }
    if (pos == 0) {
        return p.substr(0, 1);
    }
    if (pos == 2 && p.size() >= 3 && p[1] == L':') {
        return p.substr(0, 3);
    }
    return p.substr(0, pos);
}

std::wstring ModuleDirW(HMODULE module) {
    return ParentPathW(ModulePathW(module));
}

std::wstring CurrentDirectoryW() {
    const DWORD required = ::GetCurrentDirectoryW(0, nullptr);
    if (required == 0) {
        ThrowLastError("GetCurrentDirectoryW");
    }

    std::wstring buffer(required, L'\0');
    const DWORD length = ::GetCurrentDirectoryW(required, buffer.data());
    if (length == 0 || length >= required) {
        ThrowLastError("GetCurrentDirectoryW");
    }
    buffer.resize(length);
    return buffer;
}

std::wstring TempDirectoryW() {
    std::wstring buffer(MAX_PATH + 1, L'\0');
    DWORD length = ::GetTempPathW(static_cast<DWORD>(buffer.size()), buffer.data());
    if (length == 0) {
        ThrowLastError("GetTempPathW");
    }
    if (length >= buffer.size()) {
        buffer.assign(length + 1, L'\0');
        length = ::GetTempPathW(static_cast<DWORD>(buffer.size()), buffer.data());
        if (length == 0 || length >= buffer.size()) {
            ThrowLastError("GetTempPathW");
        }
    }
    buffer.resize(length);
    return buffer;
}

std::wstring FileNameW(std::wstring_view path) {
    const size_t pos = path.find_last_of(L"\\/");
    if (pos == std::wstring_view::npos) {
        return z_to_wstring(path);
    }
    return z_to_wstring(path.substr(pos + 1));
}

std::wstring JoinPathW(std::wstring_view left, std::wstring_view right) {
    if (left.empty()) {
        return z_to_wstring(right);
    }
    if (right.empty()) {
        return z_to_wstring(left);
    }

    std::wstring result = z_to_wstring(left);
    const wchar_t last = result.back();
    const wchar_t first = right.front();
    if ((last == L'\\' || last == L'/') && (first == L'\\' || first == L'/')) {
        result.append(right.substr(1));
    } else if (last == L'\\' || last == L'/' || first == L'\\' || first == L'/') {
        result.append(right);
    } else {
        result.push_back(L'\\');
        result.append(right);
    }
    return result;
}

std::wstring ToLongPathW(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    if (p.rfind(LR"(\\?\)", 0) == 0) {
        return p;
    }
    if (p.rfind(LR"(\\)", 0) == 0) {
        return LR"(\\?\UNC\)" + p.substr(2);
    }
    return LR"(\\?\)" + p;
}

std::optional<std::wstring> EnvironmentVariableW(std::wstring_view name) {
    std::wstring n = z_to_wstring(name);
    DWORD required = ::GetEnvironmentVariableW(n.c_str(), nullptr, 0);
    if (required == 0) {
        const DWORD error = ::GetLastError();
        if (error == ERROR_ENVVAR_NOT_FOUND) {
            return std::nullopt;
        }
        ThrowLastError("GetEnvironmentVariableW");
    }

    std::wstring buffer(required, L'\0');
    const DWORD length = ::GetEnvironmentVariableW(n.c_str(), buffer.data(), required);
    if (length == 0 || length >= required) {
        ThrowLastError("GetEnvironmentVariableW");
    }
    buffer.resize(length);
    return buffer;
}

void SetEnvironmentVariableWZ(std::wstring_view name,
                              std::optional<std::wstring_view> value) {
    std::wstring n = z_to_wstring(name);
    std::wstring v;
    LPCWSTR valuePtr = nullptr;
    if (value.has_value()) {
        v = z_to_wstring(*value);
        valuePtr = v.c_str();
    }
    if (!::SetEnvironmentVariableW(n.c_str(), valuePtr)) {
        ThrowLastError("SetEnvironmentVariableW");
    }
}

std::wstring ExpandEnvironmentStringsWZ(std::wstring_view text) {
    std::wstring src = z_to_wstring(text);
    const DWORD required = ::ExpandEnvironmentStringsW(src.c_str(), nullptr, 0);
    if (required == 0) {
        ThrowLastError("ExpandEnvironmentStringsW");
    }

    std::wstring buffer(required, L'\0');
    const DWORD length =
            ::ExpandEnvironmentStringsW(src.c_str(), buffer.data(), required);
    if (length == 0 || length > required) {
        ThrowLastError("ExpandEnvironmentStringsW");
    }
    buffer.resize(length > 0 ? length - 1 : 0);
    return buffer;
}

// -----------------------------------------------------------------------------
// 文件系统实现
// -----------------------------------------------------------------------------

bool PathExistsW(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    return ::GetFileAttributesW(p.c_str()) != INVALID_FILE_ATTRIBUTES;
}

bool FileExistsW(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    const DWORD attrs = ::GetFileAttributesW(p.c_str());
    return attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0;
}

bool DirectoryExistsW(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    const DWORD attrs = ::GetFileAttributesW(p.c_str());
    return attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

// 逐级创建目录，使 C:\a\b\c 这类多级路径一次完成。
bool CreateDirectoryTreeW(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    if (p.empty()) {
        return false;
    }

    const int result = ::SHCreateDirectoryExW(nullptr, p.c_str(), nullptr);
    if (result == ERROR_SUCCESS) {
        return true;
    }
    if (result == ERROR_ALREADY_EXISTS || result == ERROR_FILE_EXISTS) {
        return false;
    }
    throw Win32Error(static_cast<DWORD>(result), "SHCreateDirectoryExW");
}

// 使用 CreateFileW + ReadFile 读取整个文件，适合中小型配置/资源文件。
Bytes ReadFileBytes(std::wstring_view path) {
    std::wstring p = z_to_wstring(path);
    Handle file(::CreateFileW(
        p.c_str(),
        GENERIC_READ,
        FILE_SHARE_READ,
        nullptr,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        nullptr));

    ThrowIfInvalidHandle(file.get(), "CreateFileW");

    LARGE_INTEGER fileSize{};
    ThrowIfFalse(::GetFileSizeEx(file.get(), &fileSize), "GetFileSizeEx");
    if (fileSize.QuadPart < 0 || static_cast<unsigned long long>(fileSize.QuadPart) > (
            std::numeric_limits<size_t>::max)()) {
        throw std::length_error("file too large to read into memory");
    }

    Bytes data(static_cast<size_t>(fileSize.QuadPart));
    size_t offset = 0;
    while (offset < data.size()) {
        const DWORD chunk = z_span_chunk_size(data.size() - offset);
        DWORD read = 0;
        ThrowIfFalse(::ReadFile(file.get(), data.data() + offset, chunk, &read, nullptr),
                     "ReadFile");
        if (read == 0) {
            break;
        }
        offset += read;
    }
    data.resize(offset);
    return data;
}

// 使用 CreateFileW + WriteFile 写入字节，支持覆盖或追加。
void WriteFileBytes(std::wstring_view path, std::span<const std::byte> data,
                    bool append) {
    std::wstring p = z_to_wstring(path);
    Handle file(::CreateFileW(
        p.c_str(),
        GENERIC_WRITE,
        0,
        nullptr,
        append ? OPEN_ALWAYS : CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        nullptr));

    ThrowIfInvalidHandle(file.get(), "CreateFileW");

    if (append) {
        LARGE_INTEGER zero{};
        ThrowIfFalse(::SetFilePointerEx(file.get(), zero, nullptr, FILE_END),
                     "SetFilePointerEx");
    }

    size_t offset = 0;
    while (offset < data.size()) {
        const DWORD chunk = z_span_chunk_size(data.size() - offset);
        DWORD written = 0;
        ThrowIfFalse(
            ::WriteFile(file.get(), data.data() + offset, chunk, &written, nullptr),
            "WriteFile");
        if (written == 0) {
            throw std::runtime_error("WriteFile wrote zero bytes");
        }
        offset += written;
    }
}

std::string ReadTextFileUtf8(std::wstring_view path) {
    Bytes bytes = ReadFileBytes(path);
    size_t offset = 0;
    if (bytes.size() >= 3 &&
        static_cast<unsigned char>(bytes[0]) == 0xEF &&
        static_cast<unsigned char>(bytes[1]) == 0xBB &&
        static_cast<unsigned char>(bytes[2]) == 0xBF) {
        offset = 3;
    }

    return std::string(
        reinterpret_cast<const char *>(bytes.data() + offset),
        reinterpret_cast<const char *>(bytes.data() + bytes.size()));
}

void WriteTextFileUtf8(std::wstring_view path, std::string_view text, bool append,
                       bool writeBom) {
    if (writeBom && !append) {
        Bytes bytes;
        bytes.reserve(text.size() + 3);
        bytes.push_back(static_cast<std::byte>(0xEF));
        bytes.push_back(static_cast<std::byte>(0xBB));
        bytes.push_back(static_cast<std::byte>(0xBF));
        const auto *first = reinterpret_cast<const std::byte *>(text.data());
        bytes.insert(bytes.end(), first, first + text.size());
        WriteFileBytes(path, bytes, false);
        return;
    }

    WriteFileBytes(
        path, std::span<const std::byte>(reinterpret_cast<const std::byte *>(text.data()),
                                         text.size()), append);
}

// -----------------------------------------------------------------------------
// 进程与命令行实现
// -----------------------------------------------------------------------------

// CommandLineToArgvW 能正确解析 Windows 命令行引号和反斜杠规则。
std::vector<std::wstring> CommandLineArgsW() {
    int argc = 0;
    LPWSTR *argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
    if (!argv) {
        ThrowLastError("CommandLineToArgvW");
    }

    std::vector<std::wstring> result;
    result.reserve(static_cast<size_t>(argc));
    for (int i = 0; i < argc; ++i) {
        result.emplace_back(argv[i]);
    }
    ::LocalFree(argv);
    return result;
}

// 按 Windows C/C++ argv 解析规则转义单个参数。
std::wstring QuoteCommandLineArgW(std::wstring_view arg) {
    if (arg.empty()) {
        return L"\"\"";
    }

    const bool needQuotes = arg.find_first_of(L" \t\n\v\"") != std::wstring_view::npos;
    if (!needQuotes) {
        return z_to_wstring(arg);
    }

    std::wstring result;
    result.push_back(L'\"');
    size_t backslashes = 0;

    for (wchar_t c: arg) {
        if (c == L'\\') {
            ++backslashes;
        } else if (c == L'\"') {
            result.append(backslashes * 2 + 1, L'\\');
            result.push_back(c);
            backslashes = 0;
        } else {
            result.append(backslashes, L'\\');
            backslashes = 0;
            result.push_back(c);
        }
    }

    result.append(backslashes * 2, L'\\');
    result.push_back(L'\"');
    return result;
}

std::wstring BuildCommandLineW(std::span<const std::wstring> args) {
    std::wstring result;
    for (size_t i = 0; i < args.size(); ++i) {
        if (i != 0) {
            result.push_back(L' ');
        }
        result += QuoteCommandLineArgW(args[i]);
    }
    return result;
}

// CreateProcessW 要求命令行缓冲区可写，因此这里复制为 std::wstring。
ProcessInfo StartProcess(const ProcessStartInfo &info) {
    STARTUPINFOW si{};
    si.cb = sizeof(si);
    if (!info.showWindow) {
        si.dwFlags |= STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;
    } else if (info.showCommand != SW_SHOWNORMAL) {
        si.dwFlags |= STARTF_USESHOWWINDOW;
        si.wShowWindow = info.showCommand;
    }

    PROCESS_INFORMATION pi{};
    std::wstring app = info.application;
    std::wstring commandLine = info.commandLine;
    std::wstring cwd = info.workingDirectory;

    if (commandLine.empty() && !app.empty()) {
        commandLine = QuoteCommandLineArgW(app);
    }

    LPCWSTR appPtr = app.empty() ? nullptr : app.c_str();
    LPWSTR commandPtr = commandLine.empty() ? nullptr : commandLine.data();
    LPCWSTR cwdPtr = cwd.empty() ? nullptr : cwd.c_str();

    ThrowIfFalse(::CreateProcessW(
                     appPtr,
                     commandPtr,
                     nullptr,
                     nullptr,
                     info.inheritHandles ? TRUE : FALSE,
                     info.creationFlags,
                     nullptr,
                     cwdPtr,
                     &si,
                     &pi), "CreateProcessW");

    ProcessInfo result;
    result.process.reset(pi.hProcess);
    result.thread.reset(pi.hThread);
    result.processId = pi.dwProcessId;
    result.threadId = pi.dwThreadId;
    return result;
}

std::optional<DWORD> WaitForProcess(HANDLE process, DWORD milliseconds) {
    const DWORD waitResult = ::WaitForSingleObject(process, milliseconds);
    if (waitResult == WAIT_TIMEOUT) {
        return std::nullopt;
    }
    if (waitResult == WAIT_FAILED) {
        ThrowLastError("WaitForSingleObject");
    }

    DWORD exitCode = 0;
    ThrowIfFalse(::GetExitCodeProcess(process, &exitCode), "GetExitCodeProcess");
    return exitCode;
}

// 通过进程 Token 查询是否管理员提升运行。
bool IsProcessElevated() {
    Handle token;
    ThrowIfFalse(::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, token.put()),
                 "OpenProcessToken");

    TOKEN_ELEVATION elevation{};
    DWORD returned = 0;
    ThrowIfFalse(::GetTokenInformation(token.get(), TokenElevation, &elevation,
                                       sizeof(elevation), &returned),
                 "GetTokenInformation");
    return elevation.TokenIsElevated != 0;
}

std::wstring UserNameW() {
    DWORD size = 0;
    ::GetUserNameW(nullptr, &size);
    if (size == 0) {
        ThrowLastError("GetUserNameW");
    }

    std::wstring result(size, L'\0');
    ThrowIfFalse(::GetUserNameW(result.data(), &size), "GetUserNameW");
    if (size > 0 && result[size - 1] == L'\0') {
        --size;
    }
    result.resize(size);
    return result;
}

std::wstring ComputerNameW() {
    DWORD size = 0;
    ::GetComputerNameExW(ComputerNameDnsHostname, nullptr, &size);
    if (size == 0 && ::GetLastError() != ERROR_MORE_DATA) {
        ThrowLastError("GetComputerNameExW");
    }

    std::wstring result(size, L'\0');
    ThrowIfFalse(::GetComputerNameExW(ComputerNameDnsHostname, result.data(), &size),
                 "GetComputerNameExW");
    result.resize(size);
    return result;
}

DWORD CurrentProcessId() noexcept {
    return ::GetCurrentProcessId();
}

DWORD CurrentThreadId() noexcept {
    return ::GetCurrentThreadId();
}

// -----------------------------------------------------------------------------
// 注册表实现
// -----------------------------------------------------------------------------

RegKey::RegKey(HKEY key) noexcept : key_(key) {}

// 析构自动 RegCloseKey，移动语义用于转移 HKEY 所有权。
RegKey::~RegKey() noexcept {
    reset();
}

RegKey::RegKey(RegKey &&other) noexcept : key_(other.release()) {}

RegKey &RegKey::operator=(RegKey &&other) noexcept {
    if (this != &other) {
        reset(other.release());
    }
    return *this;
}

RegKey RegKey::Open(HKEY root, std::wstring_view subKey, REGSAM access) {
    HKEY key = nullptr;
    std::wstring s = z_to_wstring(subKey);
    z_throw_lstatus(::RegOpenKeyExW(root, s.c_str(), 0, access, &key), "RegOpenKeyExW");
    return RegKey{key};
}

std::optional<RegKey>
RegKey::TryOpen(HKEY root, std::wstring_view subKey, REGSAM access) {
    HKEY key = nullptr;
    std::wstring s = z_to_wstring(subKey);
    const LSTATUS status = ::RegOpenKeyExW(root, s.c_str(), 0, access, &key);
    if (status == ERROR_FILE_NOT_FOUND) {
        return std::nullopt;
    }
    z_throw_lstatus(status, "RegOpenKeyExW");
    return RegKey{key};
}

RegKey RegKey::Create(HKEY root, std::wstring_view subKey, REGSAM access) {
    HKEY key = nullptr;
    DWORD disposition = 0;
    std::wstring s = z_to_wstring(subKey);
    z_throw_lstatus(::RegCreateKeyExW(root, s.c_str(), 0, nullptr,
                                      REG_OPTION_NON_VOLATILE, access, nullptr, &key,
                                      &disposition), "RegCreateKeyExW");
    return RegKey{key};
}

HKEY RegKey::get() const noexcept {
    return key_;
}

HKEY *RegKey::put() noexcept {
    reset();
    return &key_;
}

bool RegKey::valid() const noexcept {
    return key_ != nullptr;
}

RegKey::operator bool() const noexcept {
    return valid();
}

HKEY RegKey::release() noexcept {
    HKEY old = key_;
    key_ = nullptr;
    return old;
}

void RegKey::reset(HKEY key) noexcept {
    if (key_) {
        ::RegCloseKey(key_);
    }
    key_ = key;
}

// 先查询注册表值大小，再按字节数读取，最后去掉结尾 NUL。
std::optional<std::wstring> RegKey::GetStringValue(std::wstring_view valueName) const {
    z_require_key(key_);
    std::wstring name = z_to_wstring(valueName);

    DWORD type = 0;
    DWORD bytes = 0;
    LSTATUS status = ::RegGetValueW(key_, nullptr, z_registry_value_name_ptr(name),
                                    RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ, &type, nullptr,
                                    &bytes);
    if (status == ERROR_FILE_NOT_FOUND) {
        return std::nullopt;
    }
    z_throw_lstatus(status, "RegGetValueW");

    if (bytes == 0) {
        return std::wstring{};
    }

    std::wstring result(bytes / sizeof(wchar_t), L'\0');
    status = ::RegGetValueW(key_, nullptr, z_registry_value_name_ptr(name),
                            RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ, &type, result.data(),
                            &bytes);
    z_throw_lstatus(status, "RegGetValueW");

    result.resize(bytes / sizeof(wchar_t));
    while (!result.empty() && result.back() == L'\0') {
        result.pop_back();
    }
    return result;
}

void RegKey::SetStringValue(std::wstring_view valueName, std::wstring_view value,
                            DWORD type) const {
    z_require_key(key_);
    if (type != REG_SZ && type != REG_EXPAND_SZ) {
        throw std::invalid_argument(
            "registry string value type must be REG_SZ or REG_EXPAND_SZ");
    }

    std::wstring name = z_to_wstring(valueName);
    std::wstring data = z_to_wstring(value);
    const DWORD bytes = static_cast<DWORD>((data.size() + 1) * sizeof(wchar_t));
    z_throw_lstatus(::RegSetValueExW(
                        key_,
                        z_registry_value_name_ptr(name),
                        0,
                        type,
                        reinterpret_cast<const BYTE *>(data.c_str()),
                        bytes), "RegSetValueExW");
}

std::optional<DWORD> RegKey::GetDwordValue(std::wstring_view valueName) const {
    z_require_key(key_);
    std::wstring name = z_to_wstring(valueName);

    DWORD type = 0;
    DWORD data = 0;
    DWORD bytes = sizeof(data);
    const LSTATUS status = ::RegGetValueW(key_, nullptr, z_registry_value_name_ptr(name),
                                          RRF_RT_REG_DWORD, &type, &data, &bytes);
    if (status == ERROR_FILE_NOT_FOUND) {
        return std::nullopt;
    }
    z_throw_lstatus(status, "RegGetValueW");
    return data;
}

void RegKey::SetDwordValue(std::wstring_view valueName, DWORD value) const {
    z_require_key(key_);
    std::wstring name = z_to_wstring(valueName);
    z_throw_lstatus(::RegSetValueExW(
                        key_,
                        z_registry_value_name_ptr(name),
                        0,
                        REG_DWORD,
                        reinterpret_cast<const BYTE *>(&value),
                        sizeof(value)), "RegSetValueExW");
}

std::optional<unsigned long long>
RegKey::GetQwordValue(std::wstring_view valueName) const {
    z_require_key(key_);
    std::wstring name = z_to_wstring(valueName);

    DWORD type = 0;
    unsigned long long data = 0;
    DWORD bytes = sizeof(data);
    const LSTATUS status = ::RegGetValueW(key_, nullptr, z_registry_value_name_ptr(name),
                                          RRF_RT_REG_QWORD, &type, &data, &bytes);
    if (status == ERROR_FILE_NOT_FOUND) {
        return std::nullopt;
    }
    z_throw_lstatus(status, "RegGetValueW");
    return data;
}

void RegKey::SetQwordValue(std::wstring_view valueName, unsigned long long value) const {
    z_require_key(key_);
    std::wstring name = z_to_wstring(valueName);
    z_throw_lstatus(::RegSetValueExW(
                        key_,
                        z_registry_value_name_ptr(name),
                        0,
                        REG_QWORD,
                        reinterpret_cast<const BYTE *>(&value),
                        sizeof(value)), "RegSetValueExW");
}

std::optional<Bytes> RegKey::GetBinaryValue(std::wstring_view valueName) const {
    z_require_key(key_);
    std::wstring name = z_to_wstring(valueName);

    DWORD type = 0;
    DWORD bytes = 0;
    LSTATUS status = ::RegQueryValueExW(key_, z_registry_value_name_ptr(name), nullptr,
                                        &type, nullptr, &bytes);
    if (status == ERROR_FILE_NOT_FOUND) {
        return std::nullopt;
    }
    z_throw_lstatus(status, "RegQueryValueExW");

    Bytes result(bytes);
    status = ::RegQueryValueExW(key_, z_registry_value_name_ptr(name), nullptr, &type,
                                reinterpret_cast<LPBYTE>(result.data()), &bytes);
    z_throw_lstatus(status, "RegQueryValueExW");
    result.resize(bytes);
    return result;
}

void RegKey::SetBinaryValue(std::wstring_view valueName,
                            std::span<const std::byte> data) const {
    z_require_key(key_);
    std::wstring name = z_to_wstring(valueName);
    if (data.size() > static_cast<size_t>((std::numeric_limits<DWORD>::max)())) {
        throw std::length_error("registry binary value too large");
    }

    z_throw_lstatus(::RegSetValueExW(
                        key_,
                        z_registry_value_name_ptr(name),
                        0,
                        REG_BINARY,
                        reinterpret_cast<const BYTE *>(data.data()),
                        static_cast<DWORD>(data.size())), "RegSetValueExW");
}

// 使用 RegEnumKeyExW 枚举子键；动态缓冲区适配不同长度名称。
std::vector<std::wstring> RegKey::EnumSubKeys() const {
    z_require_key(key_);

    DWORD subKeyCount = 0;
    DWORD maxSubKeyLength = 0;
    z_throw_lstatus(::RegQueryInfoKeyW(key_, nullptr, nullptr, nullptr, &subKeyCount,
                                       &maxSubKeyLength, nullptr, nullptr, nullptr,
                                       nullptr, nullptr, nullptr), "RegQueryInfoKeyW");

    std::vector<std::wstring> result;
    result.reserve(subKeyCount);
    std::wstring buffer(maxSubKeyLength + 1, L'\0');

    for (DWORD index = 0; index < subKeyCount; ++index) {
        DWORD length = static_cast<DWORD>(buffer.size());
        const LSTATUS status = ::RegEnumKeyExW(key_, index, buffer.data(), &length,
                                               nullptr, nullptr, nullptr, nullptr);
        if (status == ERROR_NO_MORE_ITEMS) {
            break;
        }
        z_throw_lstatus(status, "RegEnumKeyExW");
        result.emplace_back(buffer.data(), length);
    }
    return result;
}

std::vector<std::wstring> RegKey::EnumValueNames() const {
    z_require_key(key_);

    DWORD valueCount = 0;
    DWORD maxValueNameLength = 0;
    z_throw_lstatus(::RegQueryInfoKeyW(key_, nullptr, nullptr, nullptr, nullptr, nullptr,
                                       nullptr, &valueCount, &maxValueNameLength, nullptr,
                                       nullptr, nullptr), "RegQueryInfoKeyW");

    std::vector<std::wstring> result;
    result.reserve(valueCount);
    std::wstring buffer(maxValueNameLength + 1, L'\0');

    for (DWORD index = 0; index < valueCount; ++index) {
        DWORD length = static_cast<DWORD>(buffer.size());
        const LSTATUS status = ::RegEnumValueW(key_, index, buffer.data(), &length,
                                               nullptr, nullptr, nullptr, nullptr);
        if (status == ERROR_NO_MORE_ITEMS) {
            break;
        }
        z_throw_lstatus(status, "RegEnumValueW");
        result.emplace_back(buffer.data(), length);
    }
    return result;
}

// -----------------------------------------------------------------------------
// 窗口工具实现
// -----------------------------------------------------------------------------

// GetWindowTextLengthW 仅是长度提示，实际读取仍以 GetWindowTextW 返回值为准。
std::wstring WindowTextW(HWND hwnd) {
    const int length = ::GetWindowTextLengthW(hwnd);
    if (length == 0) {
        return {};
    }

    std::wstring result(static_cast<size_t>(length) + 1, L'\0');
    const int written = ::GetWindowTextW(hwnd, result.data(),
                                         static_cast<int>(result.size()));
    if (written == 0 && length > 0) {
        ThrowLastError("GetWindowTextW");
    }
    result.resize(static_cast<size_t>(written));
    return result;
}

std::string WindowTextUtf8(HWND hwnd) {
    return WideToUtf8(WindowTextW(hwnd));
}

void SetWindowTextUtf8(HWND hwnd, std::string_view text) {
    std::wstring w = Utf8ToWide(text);
    ThrowIfFalse(::SetWindowTextW(hwnd, w.c_str()), "SetWindowTextW");
}

std::wstring ClassNameW(HWND hwnd) {
    std::wstring buffer(256, L'\0');
    const int length = ::GetClassNameW(hwnd, buffer.data(),
                                       static_cast<int>(buffer.size()));
    if (length == 0) {
        ThrowLastError("GetClassNameW");
    }
    buffer.resize(static_cast<size_t>(length));
    return buffer;
}

RECT WindowRect(HWND hwnd) {
    RECT rect{};
    ThrowIfFalse(::GetWindowRect(hwnd, &rect), "GetWindowRect");
    return rect;
}

RECT ClientRect(HWND hwnd) {
    RECT rect{};
    ThrowIfFalse(::GetClientRect(hwnd, &rect), "GetClientRect");
    return rect;
}

// 根据父窗口或屏幕工作区计算目标位置，然后 SetWindowPos 移动窗口。
void CenterWindow(HWND hwnd, HWND parent) {
    const RECT window = WindowRect(hwnd);
    const int width = window.right - window.left;
    const int height = window.bottom - window.top;

    RECT area{};
    if (parent) {
        area = WindowRect(parent);
    } else {
        HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        MONITORINFO mi{};
        mi.cbSize = sizeof(mi);
        ThrowIfFalse(::GetMonitorInfoW(monitor, &mi), "GetMonitorInfoW");
        area = mi.rcWork;
    }

    const int x = area.left + ((area.right - area.left) - width) / 2;
    const int y = area.top + ((area.bottom - area.top) - height) / 2;
    ThrowIfFalse(::SetWindowPos(hwnd, nullptr, x, y, 0, 0,
                                SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE),
                 "SetWindowPos");
}

// -----------------------------------------------------------------------------
// 剪贴板与控制台实现
// -----------------------------------------------------------------------------

// 剪贴板文本使用 CF_UNICODETEXT，需要 GlobalAlloc 分配可移动内存。
void SetClipboardTextW(std::wstring_view text, HWND owner) {
    ClipboardGuard guard(owner);
    ThrowIfFalse(::EmptyClipboard(), "EmptyClipboard");

    const SIZE_T bytes = (text.size() + 1) * sizeof(wchar_t);
    HGLOBAL memory = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
    if (!memory) {
        ThrowLastError("GlobalAlloc");
    }

    void *locked = ::GlobalLock(memory);
    if (!locked) {
        ::GlobalFree(memory);
        ThrowLastError("GlobalLock");
    }

    std::memcpy(locked, text.data(), text.size() * sizeof(wchar_t));
    static_cast<wchar_t *>(locked)[text.size()] = L'\0';
    ::GlobalUnlock(memory);

    if (!::SetClipboardData(CF_UNICODETEXT, memory)) {
        ::GlobalFree(memory);
        ThrowLastError("SetClipboardData");
    }
}

// 读取 CF_UNICODETEXT；剪贴板没有 Unicode 文本时返回 nullopt。
std::optional<std::wstring> GetClipboardTextW(HWND owner) {
    if (!::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
        return std::nullopt;
    }

    ClipboardGuard guard(owner);
    HGLOBAL memory = ::GetClipboardData(CF_UNICODETEXT);
    if (!memory) {
        ThrowLastError("GetClipboardData");
    }

    const wchar_t *text = static_cast<const wchar_t *>(::GlobalLock(memory));
    if (!text) {
        ThrowLastError("GlobalLock");
    }

    std::wstring result{text};
    ::GlobalUnlock(memory);
    return result;
}

// 设置控制台代码页为 UTF-8，便于输出中文和 UTF-8 日志。
void SetConsoleUtf8() {
    ThrowIfFalse(::SetConsoleCP(CP_UTF8), "SetConsoleCP");
    ThrowIfFalse(::SetConsoleOutputCP(CP_UTF8), "SetConsoleOutputCP");
}

bool HasConsole() noexcept {
    return ::GetConsoleWindow() != nullptr;
}

bool AttachOrAllocConsole() {
    if (HasConsole()) {
        return false;
    }
    if (::AttachConsole(ATTACH_PARENT_PROCESS)) {
        return true;
    }
    const DWORD error = ::GetLastError();
    if (error == ERROR_ACCESS_DENIED) {
        return false;
    }
    ThrowIfFalse(::AllocConsole(), "AllocConsole");
    return true;
}

} // namespace Z
