#pragma once
#include <windows.h>
#include <winhttp.h>
#include <wincrypt.h>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>

#pragma comment(lib, "winhttp.lib")
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "advapi32.lib")

namespace ErenAuth {

static std::string g_BaseUrl;
static std::string g_ApiToken;
static std::string g_SessionToken;
static const char* AES_KEY = "92502cdd674d410ef37894c11fe9edfaf90de1cba06ff1b9b28f6a53b93479b8";

// ── Helpers ──────────────────────────────────────────────────────────────
inline std::string WCharToUtf8(const std::wstring& wstr) {
    if (wstr.empty()) return "";
    int len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    std::string result(len - 1, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &result[0], len, nullptr, nullptr);
    return result;
}

inline std::wstring Utf8ToWChar(const std::string& str) {
    if (str.empty()) return L"";
    int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
    std::wstring result(len - 1, 0);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &result[0], len);
    return result;
}

inline std::string HexEncode(const uint8_t* data, size_t len) {
    std::ostringstream oss;
    for (size_t i = 0; i < len; i++)
        oss << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
    return oss.str();
}

inline std::vector<uint8_t> HexDecode(const std::string& hex) {
    std::vector<uint8_t> bytes;
    for (size_t i = 0; i < hex.length(); i += 2)
        bytes.push_back((uint8_t)strtol(hex.substr(i, 2).c_str(), nullptr, 16));
    return bytes;
}

inline std::string Sha256(const std::string& input) {
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    BYTE hash[32];
    DWORD hashLen = 32;

    CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
    CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
    CryptHashData(hHash, (BYTE*)input.c_str(), (DWORD)input.length(), 0);
    CryptGetHashParam(hHash, HP_HASHVAL, hash, &hashLen, 0);
    CryptDestroyHash(hHash);
    CryptReleaseContext(hProv, 0);

    return HexEncode(hash, 32);
}

// ── HWID ──────────────────────────────────────────────────────────────────
inline std::string GetHwid() {
    HANDLE hToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
        return "UNKNOWN_SID";

    DWORD size = 0;
    GetTokenInformation(hToken, TokenUser, nullptr, 0, &size);
    std::vector<BYTE> buf(size);
    PTOKEN_USER pUser = (PTOKEN_USER)buf.data();

    if (!GetTokenInformation(hToken, TokenUser, pUser, size, &size)) {
        CloseHandle(hToken);
        return "UNKNOWN_SID";
    }
    CloseHandle(hToken);

    LPSTR sidStr;
    ConvertSidToStringSidA(pUser->User.Sid, &sidStr);
    std::string sid(sidStr);
    LocalFree(sidStr);

    return Sha256(sid);
}

// ── HTTP ──────────────────────────────────────────────────────────────────
inline std::string HttpPost(const std::string& url, const std::string& body, bool useAuth = true) {
    std::wstring wUrl = Utf8ToWChar(url);
    URL_COMPONENTS urlComp = { 0 };
    urlComp.dwStructSize = sizeof(urlComp);

    wchar_t host[256] = { 0 }, path[1024] = { 0 };
    urlComp.lpszHostName = host; urlComp.dwHostNameLength = 256;
    urlComp.lpszUrlPath = path; urlComp.dwUrlPathLength = 1024;
    WinHttpCrackUrl(wUrl.c_str(), 0, 0, &urlComp);

    HINTERNET hSession = WinHttpOpen(L"ErenAuth/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, nullptr, nullptr, 0);
    HINTERNET hConnect = WinHttpConnect(hSession, host, urlComp.nPort, 0);
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, nullptr, nullptr, nullptr,
        urlComp.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);

    std::string fullHeaders = "Content-Type: application/json\r\n";
    if (useAuth && !g_ApiToken.empty())
        fullHeaders = "Authorization: Bearer " + g_ApiToken + "\r\n" + fullHeaders;

    std::wstring wHeaders = Utf8ToWChar(fullHeaders);
    WinHttpAddRequestHeaders(hRequest, wHeaders.c_str(), (DWORD)-1, WINHTTP_ADDREQ_FLAG_ADD);

    WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
        (LPVOID)body.c_str(), (DWORD)body.length(), (DWORD)body.length(), 0);
    WinHttpReceiveResponse(hRequest, nullptr);

    std::string response;
    DWORD bytesRead;
    char buffer[4096];
    while (WinHttpReadData(hRequest, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0)
        response.append(buffer, bytesRead);

    WinHttpCloseHandle(hRequest);
    WinHttpCloseHandle(hConnect);
    WinHttpCloseHandle(hSession);
    return response;
}

// ── AES-256-CBC ──────────────────────────────────────────────────────────
inline std::string EncryptAES(const std::string& plaintext) {
    auto key = HexDecode(AES_KEY);
    std::vector<BYTE> iv(16);
    HCRYPTPROV hProv;
    CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
    CryptGenRandom(hProv, 16, iv.data());

    HCRYPTKEY hKey;
    struct { BLOBHEADER hdr; DWORD len; BYTE key[32]; } keyBlob;
    keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
    keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
    keyBlob.hdr.reserved = 0;
    keyBlob.hdr.aiKeyAlg = CALG_AES_256;
    keyBlob.len = 32;
    memcpy(keyBlob.key, key.data(), 32);
    CryptImportKey(hProv, (BYTE*)&keyBlob, sizeof(keyBlob), 0, 0, &hKey);

    DWORD mode = CRYPT_MODE_CBC;
    CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&mode, 0);
    CryptSetKeyParam(hKey, KP_IV, iv.data(), 0);

    std::vector<BYTE> data(plaintext.begin(), plaintext.end());
    DWORD encLen = (DWORD)data.size();
    CryptEncrypt(hKey, 0, TRUE, 0, nullptr, &encLen, 0);
    data.resize(encLen);
    memcpy(data.data(), plaintext.data(), plaintext.size());
    encLen = (DWORD)plaintext.size();
    CryptEncrypt(hKey, 0, TRUE, 0, data.data(), &encLen, (DWORD)data.size());
    data.resize(encLen);

    CryptDestroyKey(hKey);
    CryptReleaseContext(hProv, 0);

    return HexEncode(iv.data(), 16) + ":" + HexEncode(data.data(), encLen);
}

inline std::string DecryptAES(const std::string& encrypted) {
    auto pos = encrypted.find(':');
    if (pos == std::string::npos) return "";
    auto iv = HexDecode(encrypted.substr(0, pos));
    auto data = HexDecode(encrypted.substr(pos + 1));
    auto key = HexDecode(AES_KEY);

    HCRYPTPROV hProv;
    CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);

    HCRYPTKEY hKey;
    struct { BLOBHEADER hdr; DWORD len; BYTE key[32]; } keyBlob;
    keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
    keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
    keyBlob.hdr.reserved = 0;
    keyBlob.hdr.aiKeyAlg = CALG_AES_256;
    keyBlob.len = 32;
    memcpy(keyBlob.key, key.data(), 32);
    CryptImportKey(hProv, (BYTE*)&keyBlob, sizeof(keyBlob), 0, 0, &hKey);

    DWORD mode = CRYPT_MODE_CBC;
    CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&mode, 0);
    CryptSetKeyParam(hKey, KP_IV, iv.data(), 0);

    DWORD dataLen = (DWORD)data.size();
    CryptDecrypt(hKey, 0, TRUE, 0, data.data(), &dataLen);

    CryptDestroyKey(hKey);
    CryptReleaseContext(hProv, 0);

    return std::string((char*)data.data(), dataLen);
}

// ── JSON Simple ───────────────────────────────────────────────────────────
inline std::string JsonGet(const std::string& json, const std::string& key) {
    std::string search = "\"" + key + "\"";
    auto pos = json.find(search);
    if (pos == std::string::npos) return "";

    pos = json.find(':', pos + search.length());
    if (pos == std::string::npos) return "";

    pos = json.find_first_not_of(" \t\n\r", pos + 1);
    if (pos == std::string::npos) return "";

    if (json[pos] == '"') {
        pos++;
        auto end = json.find('"', pos);
        if (end == std::string::npos) return "";
        return json.substr(pos, end - pos);
    }

    auto end = json.find_first_of(",}\n\r", pos);
    if (end == std::string::npos) return "";
    return json.substr(pos, end - pos);
}

inline std::string JsonGetNested(const std::string& json, const std::string& outerKey, const std::string& innerKey) {
    std::string search = "\"" + outerKey + "\"";
    auto pos = json.find(search);
    if (pos == std::string::npos) return "";

    pos = json.find('{', pos);
    if (pos == std::string::npos) return "";

    int depth = 1;
    size_t end = pos + 1;
    while (end < json.length() && depth > 0) {
        if (json[end] == '{') depth++;
        else if (json[end] == '}') depth--;
        end++;
    }

    return JsonGet(json.substr(pos, end - pos), innerKey);
}

// ── API ───────────────────────────────────────────────────────────────────
inline void Initialize(const std::string& baseUrl, const std::string& apiToken) {
    g_BaseUrl = baseUrl;
    while (!g_BaseUrl.empty() && g_BaseUrl.back() == '/')
        g_BaseUrl.pop_back();
    g_ApiToken = apiToken;
}

inline std::string Login(const std::string& username, const std::string& password) {
    std::string hwid = GetHwid();
    std::string body = "{\"action\":\"login\",\"username\":\"" + username +
        "\",\"password\":\"" + password + "\",\"hwid\":\"" + hwid + "\"}";
    std::string resp = HttpPost(g_BaseUrl + "/fetch", body);
    g_SessionToken = JsonGet(resp, "token");
    return resp;
}

inline std::string FetchUser(const std::string& username) {
    std::string payload = EncryptAES("{\"username\":\"" + username + "\"}");
    std::string body = "{\"action\":\"data\",\"payload\":\"" + payload + "\"}";
    std::string resp = HttpPost(g_BaseUrl + "/fetch", body);
    return DecryptAES(resp);
}

inline std::string AutoLogin() {
    std::string hwid = GetHwid();
    std::string payload = EncryptAES("{\"hwid\":\"" + hwid + "\"}");
    std::string body = "{\"action\":\"autologin\",\"payload\":\"" + payload + "\"}";
    std::string resp = HttpPost(g_BaseUrl + "/fetch", body);
    return DecryptAES(resp);
}

inline bool ValidateSession() {
    if (g_SessionToken.empty()) return false;
    std::string hwid = GetHwid();
    // validateToken is on BASE url (not /fetch)
    std::string authHeader = "Authorization: Bearer " + g_SessionToken + "\r\nx-hwid: " + hwid + "\r\nContent-Type: application/json\r\n";

    std::wstring wUrl = Utf8ToWChar(g_BaseUrl + "/validateToken");
    URL_COMPONENTS urlComp = { 0 };
    urlComp.dwStructSize = sizeof(urlComp);
    wchar_t host[256] = { 0 }, path[1024] = { 0 };
    urlComp.lpszHostName = host; urlComp.dwHostNameLength = 256;
    urlComp.lpszUrlPath = path; urlComp.dwUrlPathLength = 1024;
    WinHttpCrackUrl(wUrl.c_str(), 0, 0, &urlComp);

    HINTERNET hSession = WinHttpOpen(L"ErenAuth/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, nullptr, nullptr, 0);
    HINTERNET hConnect = WinHttpConnect(hSession, host, urlComp.nPort, 0);
    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, nullptr, nullptr, nullptr, urlComp.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0);

    std::wstring wHeaders = Utf8ToWChar(authHeader);
    WinHttpAddRequestHeaders(hRequest, wHeaders.c_str(), (DWORD)-1, WINHTTP_ADDREQ_FLAG_ADD);
    WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, nullptr, 0, 0, 0);
    WinHttpReceiveResponse(hRequest, nullptr);

    DWORD statusCode = 0, size = sizeof(statusCode);
    WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, nullptr, &statusCode, &size, nullptr);

    WinHttpCloseHandle(hRequest);
    WinHttpCloseHandle(hConnect);
    WinHttpCloseHandle(hSession);

    return statusCode == 200;
}

inline void Logout() {
    g_SessionToken.clear();
}

inline std::string GetSessionToken() { return g_SessionToken; }
inline bool IsLoggedIn() { return !g_SessionToken.empty(); }

} // namespace ErenAuth
