#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <bcrypt.h>
#include <intrin.h>

#include "security/hwid_generator.hpp"

#include <array>
#include <format>
#include <memory>
#include <mutex>
#include <vector>

#pragma comment(lib, "bcrypt.lib")

namespace poe2::security {

namespace {

// RAII Wrapper cho BCrypt Handle
struct BCryptAlgCloser {
    void operator()(BCRYPT_ALG_HANDLE h) const noexcept {
        if (h) BCryptCloseAlgorithmProvider(h, 0);
    }
};

struct BCryptHashCloser {
    void operator()(BCRYPT_HASH_HANDLE h) const noexcept {
        if (h) BCryptDestroyHash(h);
    }
};

using UniqueAlgHandle = std::unique_ptr<void, BCryptAlgCloser>;
using UniqueHashHandle = std::unique_ptr<void, BCryptHashCloser>;

std::string GetRegistryMachineGuid() {
    HKEY hKey = nullptr;
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ | KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS) {
        return "";
    }

    wchar_t guidBuffer[128] = {0};
    DWORD bufSize = sizeof(guidBuffer);
    DWORD type = REG_SZ;
    std::string result;

    if (RegQueryValueExW(hKey, L"MachineGuid", nullptr, &type, reinterpret_cast<LPBYTE>(guidBuffer), &bufSize) == ERROR_SUCCESS) {
        char mbBuf[128] = {0};
        int len = WideCharToMultiByte(CP_UTF8, 0, guidBuffer, -1, mbBuf, sizeof(mbBuf), nullptr, nullptr);
        if (len > 0) {
            result = std::string(mbBuf, len - 1);
        }
    }
    RegCloseKey(hKey);
    return result;
}

std::string GetVolumeSerial() {
    DWORD serialNumber = 0;
    if (GetVolumeInformationW(L"C:\\", nullptr, 0, &serialNumber, nullptr, nullptr, nullptr, 0)) {
        return std::format("{:08X}", serialNumber);
    }
    return "";
}

std::string GetCpuProcessorId() {
    int cpuInfo[4] = {0};
    __cpuid(cpuInfo, 1);
    // EAX + EDX chứa processor signature và feature flags
    return std::format("{:08X}{:08X}", static_cast<uint32_t>(cpuInfo[3]), static_cast<uint32_t>(cpuInfo[0]));
}

std::string GetSmbiosUuid() {
    const DWORD sig = 'RSMB';
    DWORD size = GetSystemFirmwareTable(sig, 0, nullptr, 0);
    if (size == 0) {
        return "";
    }

    std::vector<BYTE> buffer(size);
    if (GetSystemFirmwareTable(sig, 0, buffer.data(), size) != size) {
        return "";
    }

    // Cấu trúc thô SMBIOS: duyệt qua các bảng Type 1 (System Information)
    // Cấu trúc Type 1 chứa UUID tại offset 0x08 (độ dài 16 bytes)
    if (size < 8) return "";

    // Bỏ qua header cấu trúc Raw SMBIOS (thường bắt đầu tại offset 8)
    const BYTE* ptr = buffer.data() + 8;
    const BYTE* end = buffer.data() + size;

    while (ptr + 4 <= end) {
        BYTE type = ptr[0];
        BYTE length = ptr[1];
        if (length < 4 || ptr + length > end) break;

        if (type == 1 && length >= 0x18) {
            // Type 1 System Information: UUID tại offset 8 (16 bytes)
            const BYTE* uuid = ptr + 8;
            bool allZero = true;
            bool allFF = true;
            for (int i = 0; i < 16; ++i) {
                if (uuid[i] != 0x00) allZero = false;
                if (uuid[i] != 0xFF) allFF = false;
            }
            if (!allZero && !allFF) {
                return std::format(
                    "{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
                    uuid[0], uuid[1], uuid[2], uuid[3],
                    uuid[4], uuid[5],
                    uuid[6], uuid[7],
                    uuid[8], uuid[9],
                    uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]
                );
            }
        }

        // Nhảy qua phần chuỗi kết thúc bởi double null (0x00 0x00)
        ptr += length;
        while (ptr + 1 < end && !(ptr[0] == 0 && ptr[1] == 0)) {
            ptr++;
        }
        ptr += 2; // bỏ qua 2 byte null
    }

    return "";
}

} // namespace

std::string ComputeSha256Hex(std::string_view input) {
    BCRYPT_ALG_HANDLE rawAlg = nullptr;
    if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&rawAlg, BCRYPT_SHA256_ALGORITHM, nullptr, 0))) {
        return "";
    }
    UniqueAlgHandle alg(rawAlg);

    DWORD hashObjSize = 0;
    DWORD cbData = 0;
    if (!BCRYPT_SUCCESS(BCryptGetProperty(alg.get(), BCRYPT_OBJECT_LENGTH, reinterpret_cast<PBYTE>(&hashObjSize), sizeof(DWORD), &cbData, 0))) {
        return "";
    }

    std::vector<BYTE> hashObject(hashObjSize);
    BCRYPT_HASH_HANDLE rawHash = nullptr;
    if (!BCRYPT_SUCCESS(BCryptCreateHash(alg.get(), &rawHash, hashObject.data(), hashObjSize, nullptr, 0, 0))) {
        return "";
    }
    UniqueHashHandle hash(rawHash);

    if (!BCRYPT_SUCCESS(BCryptHashData(hash.get(), reinterpret_cast<PBYTE>(const_cast<char*>(input.data())), static_cast<ULONG>(input.size()), 0))) {
        return "";
    }

    std::array<BYTE, 32> hashValue = {0};
    if (!BCRYPT_SUCCESS(BCryptFinishHash(hash.get(), hashValue.data(), static_cast<ULONG>(hashValue.size()), 0))) {
        return "";
    }

    std::string hex;
    hex.reserve(64);
    for (BYTE b : hashValue) {
        hex += std::format("{:02X}", b);
    }
    return hex;
}

HardwareDetails GetHardwareDetails() {
    static HardwareDetails cached;
    static std::once_flag flag;
    std::call_once(flag, []() {
        cached.machineGuid = GetRegistryMachineGuid();
        cached.motherboardUuid = GetSmbiosUuid();
        if (cached.motherboardUuid.empty() || cached.motherboardUuid == "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") {
            cached.motherboardUuid = cached.machineGuid.empty() ? "MB-DEFAULT-UUID" : cached.machineGuid;
        }
        cached.cpuId = GetCpuProcessorId();
        cached.diskSerial = GetVolumeSerial();
        if (cached.diskSerial.empty()) {
            cached.diskSerial = "DISK-DEFAULT-SERIAL";
        }
    });
    return cached;
}

std::string GetMachineHWID(std::string_view customSalt) {
    const std::string_view salt = customSalt.empty() ? DEFAULT_HWID_SALT : customSalt;
    const HardwareDetails details = GetHardwareDetails();

    std::string rawFingerprint = std::format(
        "{}|{}|{}|{}|{}",
        details.motherboardUuid,
        details.cpuId,
        details.diskSerial,
        details.machineGuid,
        salt
    );

    // Băm 2 vòng SHA-256 tương thích với hwid.py
    std::string firstPassHex = ComputeSha256Hex(rawFingerprint);
    // Vòng 2: firstPassHex + salt
    std::string finalHashHex = ComputeSha256Hex(firstPassHex + std::string(salt));

    if (finalHashHex.size() < 16) {
        return "PVT-0000-0000-0000-0000";
    }

    std::string h = finalHashHex.substr(0, 16);
    return std::format("PVT-{}-{}-{}-{}",
        h.substr(0, 4),
        h.substr(4, 4),
        h.substr(8, 4),
        h.substr(12, 4)
    );
}

} // namespace poe2::security
