#define NOMINMAX
#include <Windows.h>

#include <iostream>
#include <thread>
#include <chrono>

#include "Driver/Driver.h"
#include "Globals.hxx"
#include "Miscellaneous/Output/Output.h"
#include "Core/Graphics/Graphics.h"
#include "Engine/Engine.h"
#include "Core/Features/Cache/Cache.h"
#include "Core/Features/Cheats/Misc/Misc.h"
#include "Core/Features/Cheats/World/World.h"
#include "Core/Features/Cheats/Aimbot/Aimbot.h"
#include "Core/Features/Cheats/Aimbot/Silent/Silent.h"
#include "Core/Features/Cheats/Hitbox/Hitbox.h"
#include "Core/Features/ConfigManager/ConfigManager.h"

#include <ShlObj.h>
#pragma comment(lib, "Shell32.lib")

HWND g_RobloxWindow = NULL;

std::int32_t main(std::int32_t argc, char** argv[])
{
    SetConsoleTitleA("Fang.wtf");

    static constexpr const char* BINARY_NAME = "RobloxPlayerBeta.exe";

    // FIX: Сначала ищем процесс, потом проверяем
    std::uint32_t PID = Driver->Find_Process(BINARY_NAME);
    if (PID == 0) {
        MessageBoxA(NULL,
            "Roblox process not found!\nMake sure Roblox is running.",
            "Error", MB_OK | MB_ICONERROR
        );
        return 0;
    }

    // FIX: Пытаемся подключиться
    if (!Driver->Attach_Process(BINARY_NAME)) {
        DWORD error = GetLastError();
        char msg[256];
        sprintf_s(msg,
            "Failed to attach!\n"
            "Error code: %lu\n"
            "Process ID: %u\n\n"
            "Try running as Administrator",
            error, PID
        );
        MessageBoxA(NULL, msg, "Error", MB_OK | MB_ICONERROR);
        return 0;
    }

    // FIX: Проверяем хендл правильно (NULL, а не 0)
    if (Driver->Get_Handle() == NULL) {
        DWORD error = GetLastError();
        char msg[256];
        sprintf_s(msg, "Handle is NULL! Error code: %lu", error);
        MessageBoxA(NULL, msg, "Error", MB_OK | MB_ICONERROR);
        return 0;
    }

    // Находим модуль
    if (Driver->Find_Module(BINARY_NAME) == 0) {
        MessageBoxA(NULL,
            "Failed to find module!\nBase address is 0.",
            "Error", MB_OK | MB_ICONERROR
        );
        return 0;
    }

    // Читаем глобальные адреса
    auto FakeDataModel = Driver->Read<std::uint64_t>(
        Driver->Get_Module() + Offsets::FakeDataModel::Pointer
    );
    Globals::Datamodel.Address = Driver->Read<std::uint64_t>(
        FakeDataModel + Offsets::FakeDataModel::RealDataModel
    );
    Globals::VisualEngine.Address = Driver->Read<std::uint64_t>(
        Driver->Get_Module() + Offsets::VisualEngine::Pointer
    );
    Globals::Players.Address = Globals::Datamodel
        .Find_First_Child_Of_Class("Players").Address;
    Globals::Workspace.Address = Globals::Datamodel
        .Find_First_Child_Of_Class("Workspace").Address;
    Globals::Camera.Address = Globals::Workspace
        .Find_First_Child_Of_Class("Camera").Address;

    auto Lightin = Globals::Datamodel.Find_First_Child_Of_Class("Lighting");
    Globals::Lighting = SDK::Lighting(Lightin.Address);

    // FIX: Инициализируем LocalPlayer правильно
    if (Globals::Players.Address != 0) {
        SDK::Players PlayersInst(Globals::Players.Address);
        SDK::Players LP = PlayersInst.Get_Local_Player();
        if (LP.Address != 0) {
            Globals::LocalPlayer.Address = LP.Address;
            // FIX: Читаем Character через оффсет, а не через поле
            Globals::LocalPlayer.Character.Address = Driver->Read<std::uint64_t>(
                LP.Address + Offsets::Player::ModelInstance
            );
            // FIX: Имя читаем через метод Instance
            Globals::LocalPlayer.Name = LP.Name.empty()
                ? SDK::Instance(LP.Address).Name()
                : LP.Name;
        }
    }

    // Выводим информацию
    Output::Success("Process Found        -> %u", Driver->Get_Process());
    Output::Success("Attached To Process  -> 0x%llx", (uint64_t)Driver->Get_Handle());
    Output::Success("Base Module Address  -> 0x%llx", Driver->Get_Module());
    Output::Fang("Datamodel Address    -> 0x%llx", Globals::Datamodel.Address);
    Output::Fang("VisualEngine Address -> 0x%llx", Globals::VisualEngine.Address);
    Output::Fang("Players Address      -> 0x%llx", Globals::Players.Address);
    Output::Fang("LocalPlayer Address  -> 0x%llx", Globals::LocalPlayer.Address);
    Output::Fang("LocalPlayer Char     -> 0x%llx", Globals::LocalPlayer.Character.Address);
    Output::Fang("Workspace Address    -> 0x%llx", Globals::Workspace.Address);
    Output::Fang("Camera Address       -> 0x%llx", Globals::Camera.Address);
    Output::Fang("Lighting Address     -> 0x%llx", Globals::Lighting.Address);

    // Ищем окно Roblox
    g_RobloxWindow = FindWindowA(NULL, "Roblox");
    if (!g_RobloxWindow) {
        g_RobloxWindow = FindWindowA("Win32Window", NULL);
    }
    if (!g_RobloxWindow) {
        MessageBoxA(NULL, "Could not find Roblox window!", "Error", MB_OK);
        return 0;
    }

    // Запускаем сервисы в отдельных потоках
    std::thread(Cache::RunService).detach();
    std::thread(World::RunService).detach();
    std::thread(Misc::RunService).detach();
    std::thread(Aimbot::RunService).detach();
    std::thread(Silent::RunService).detach();
    std::thread(Hitbox::RunService).detach();

    // FIX: Проверяем Workspace перед записью
    if (Globals::Workspace.Address != 0) {
        auto workspacetoworld = Driver->Read<uintptr_t>(
            Globals::Workspace.Address + 0x3D8
        );
        if (workspacetoworld != 0) {
            Driver->Write<float>(workspacetoworld + 0x658, 200 * 4.f);
        }
    }

    // Создаём графику
    if (!Graphic->Create_Window()) {
        MessageBoxA(NULL, "Failed to create window!", "Error", MB_OK);
        return 0;
    }
    if (!Graphic->Create_Device()) {
        MessageBoxA(NULL, "Failed to create D3D Device!", "Error", MB_OK);
        return 0;
    }
    if (!Graphic->Create_Imgui()) {
        MessageBoxA(NULL, "Failed to init ImGui!", "Error", MB_OK);
        return 0;
    }

    Config::Manager::Init();

    // Главный цикл рендера
    for (;;) {
        // Проверяем что Roblox ещё жив
        if (!IsWindow(g_RobloxWindow)) {
            Output::Fang("Roblox window closed, exiting...");
            break;
        }

        // Обновляем VisualEngine каждый кадр (может меняться)
        if (Driver->Get_Module() != 0) {
            Globals::VisualEngine.Address = Driver->Read<std::uint64_t>(
                Driver->Get_Module() + Offsets::VisualEngine::Pointer
            );
        }

        // Синхронизируем позицию и размер оверлея с окном Roblox
        RECT robloxRect;
        if (GetWindowRect(g_RobloxWindow, &robloxRect)) {
            int w = robloxRect.right - robloxRect.left;
            int h = robloxRect.bottom - robloxRect.top;
            if (w > 0 && h > 0) {
                MoveWindow(
                    Graphic->Detail->Window,
                    robloxRect.left, robloxRect.top,
                    w, h,
                    FALSE // FALSE быстрее чем TRUE
                );
            }
        }

        // Рендерим
        Graphic->Start_Render();
        Graphic->Render_Visuals();
        if (Graphic->Running) {
            Graphic->Render_Menu();
        }
        Graphic->End_Render();

        // Небольшая пауза
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }

    return 0;
}