#include "common/window_utils.hpp"
#include "input/kmbox_net.hpp"
#include <tlhelp32.h>
#include <chrono>
#include <thread>

namespace common {

bool IsPoe2WindowHwnd(HWND hwnd) noexcept {
    if (!hwnd || !IsWindow(hwnd)) return false;

    // 1. Kiểm tra Window Class: Chỉ chấp nhận POEWindowClass hoặc POEC2_MainWindow
    wchar_t clsName[64] = {};
    if (GetClassNameW(hwnd, clsName, 64) == 0) return false;
    if (_wcsicmp(clsName, L"POEWindowClass") != 0 && _wcsicmp(clsName, L"POEC2_MainWindow") != 0) {
        return false;
    }

    // 2. Kiểm tra Process ID & Exe Name của tiến trình sở hữu HWND
    DWORD pid = 0;
    GetWindowThreadProcessId(hwnd, &pid);
    if (pid == 0) return false;

    HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
    if (!hProc) {
        hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
    }
    if (!hProc) {
        // Fallback: nếu không mở được handle do đặc quyền Admin của game,
        // nhưng Window Class đã khớp POEWindowClass/POEC2_MainWindow thì chấp nhận
        return true;
    }

    wchar_t exePath[MAX_PATH] = {};
    DWORD pathLen = MAX_PATH;
    bool match = false;
    if (QueryFullProcessImageNameW(hProc, 0, exePath, &pathLen)) {
        const wchar_t* pFileName = wcsrchr(exePath, L'\\');
        const wchar_t* exeName = pFileName ? pFileName + 1 : exePath;
        if (_wcsicmp(exeName, L"PathOfExile.exe") == 0 ||
            _wcsicmp(exeName, L"PathOfExileSteam.exe") == 0 ||
            _wcsicmp(exeName, L"PathOfExile2.exe") == 0 ||
            _wcsicmp(exeName, L"PathOfExile_x64.exe") == 0 ||
            _wcsicmp(exeName, L"PathOfExile_x64Steam.exe") == 0) {
            match = true;
        }
    } else {
        match = true;
    }
    CloseHandle(hProc);
    return match;
}

HWND FindPoe2Window() noexcept {
    HWND hwnd = FindWindowW(L"POEWindowClass", nullptr);
    if (hwnd && IsPoe2WindowHwnd(hwnd)) return hwnd;
    hwnd = FindWindowW(L"POEC2_MainWindow", nullptr);
    if (hwnd && IsPoe2WindowHwnd(hwnd)) return hwnd;
    hwnd = FindWindowW(nullptr, L"Path of Exile 2");
    if (hwnd && IsPoe2WindowHwnd(hwnd)) return hwnd;
    hwnd = FindWindowW(nullptr, L"Path of Exile");
    if (hwnd && IsPoe2WindowHwnd(hwnd)) return hwnd;
    return nullptr;
}

bool EnsurePoe2WindowFocus(uint32_t timeoutMs) noexcept {
    if (KMBoxNet::IsAnyIgnoreWindowFocus()) return true;
    HWND hwnd = FindPoe2Window();
    if (!hwnd) return false;

    HWND fg = GetForegroundWindow();
    if (fg != hwnd) {
        DWORD curThread = GetCurrentThreadId();
        DWORD fgThread = GetWindowThreadProcessId(fg, nullptr);
        AttachThreadInput(curThread, fgThread, TRUE);
        SetForegroundWindow(hwnd);
        BringWindowToTop(hwnd);
        AttachThreadInput(curThread, fgThread, FALSE);
    }

    if (GetForegroundWindow() == hwnd) return true;
    if (timeoutMs == 0) return (GetForegroundWindow() == hwnd);

    uint32_t elapsed = 0;
    while (elapsed < timeoutMs) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        elapsed += 10;
        if (GetForegroundWindow() == hwnd) return true;
    }
    return (GetForegroundWindow() == hwnd);
}

bool GetGameClientRect(HWND hwnd, RECT& outRect) noexcept {
    if (!hwnd || !IsWindow(hwnd)) return false;
    RECT rc{};
    if (!GetClientRect(hwnd, &rc)) return false;

    POINT ptTopLeft{ rc.left, rc.top };
    POINT ptBottomRight{ rc.right, rc.bottom };
    if (!ClientToScreen(hwnd, &ptTopLeft) || !ClientToScreen(hwnd, &ptBottomRight)) {
        return false;
    }
    outRect.left = ptTopLeft.x;
    outRect.top = ptTopLeft.y;
    outRect.right = ptBottomRight.x;
    outRect.bottom = ptBottomRight.y;
    return true;
}

float GetDpiScale(HWND hwnd) noexcept {
    if (!hwnd || !IsWindow(hwnd)) return 1.0f;

    using PFN_GetDpiForWindow = UINT(WINAPI*)(HWND);
    static PFN_GetDpiForWindow pfnGetDpiForWindow = reinterpret_cast<PFN_GetDpiForWindow>(
        GetProcAddress(GetModuleHandleW(L"user32.dll"), "GetDpiForWindow"));

    if (pfnGetDpiForWindow) {
        UINT dpi = pfnGetDpiForWindow(hwnd);
        if (dpi > 0) return static_cast<float>(dpi) / 96.0f;
    }

    HDC hdc = GetDC(hwnd);
    if (hdc) {
        int dpi = GetDeviceCaps(hdc, LOGPIXELSX);
        ReleaseDC(hwnd, hdc);
        if (dpi > 0) return static_cast<float>(dpi) / 96.0f;
    }
    return 1.0f;
}

std::wstring GetExecutableDirectory() noexcept {
    wchar_t exePath[MAX_PATH] = {};
    GetModuleFileNameW(nullptr, exePath, MAX_PATH);
    std::wstring dir(exePath);
    const auto slash = dir.find_last_of(L"\\/");
    return (slash == std::wstring::npos) ? L"." : dir.substr(0, slash);
}

void GetGameResolution(int& outW, int& outH) noexcept {
    HWND hwnd = FindPoe2Window();
    if (hwnd) {
        RECT rc{};
        if (GetGameClientRect(hwnd, rc)) {
            int w = rc.right - rc.left;
            int h = rc.bottom - rc.top;
            if (w > 200 && h > 200) {
                outW = w;
                outH = h;
                return;
            }
        }
    }
    outW = GetSystemMetrics(SM_CXSCREEN);
    outH = GetSystemMetrics(SM_CYSCREEN);
    if (outW <= 0) outW = 1920;
    if (outH <= 0) outH = 1080;
}

bool IsMutexActive(const std::wstring& mutexName) noexcept {
    if (mutexName.empty()) return false;
    HANDLE hMutex = OpenMutexW(SYNCHRONIZE, FALSE, mutexName.c_str());
    if (hMutex != nullptr) {
        CloseHandle(hMutex);
        return true;
    }
    return false;
}

bool IsProcessRunning(std::wstring_view exeName) noexcept {
    if (exeName.empty()) return false;
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot == INVALID_HANDLE_VALUE) return false;

    PROCESSENTRY32W pe32{};
    pe32.dwSize = sizeof(PROCESSENTRY32W);

    bool running = false;
    if (Process32FirstW(snapshot, &pe32)) {
        do {
            if (_wcsicmp(pe32.szExeFile, std::wstring(exeName).c_str()) == 0) {
                running = true;
                break;
            }
        } while (Process32NextW(snapshot, &pe32));
    }
    CloseHandle(snapshot);
    return running;
}

} // namespace common
