#include "Cache.h"
#include <vector>
#include <thread>
#include <string>
#include "Globals.hxx"
#include <mutex>
#include <algorithm>

namespace Cache {

    static std::vector<SDK::Players> GetPlayerList() {
        std::vector<SDK::Players> result;
        if (!Globals::Players.Address) return result;
        auto children = Globals::Players.Children();
        for (const auto& child : children) {
            if (child.Address == 0) continue;
            result.emplace_back(SDK::Players(child.Address));
        }
        return result;
    }

    void CacheUpdateLoop() {
        for (;;) {
            std::this_thread::sleep_for(std::chrono::milliseconds(250));

            // Обновляем 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;

                    if (!Driver) continue;
                    std::uint64_t charAddr = Driver->Read<std::uint64_t>(
                        LP.Address + Offsets::Player::ModelInstance
                    );
                    Globals::LocalPlayer.Character.Address = charAddr;
                    Globals::LocalPlayer.Name = LP.Name.empty()
                        ? SDK::Instance(LP.Address).Name()
                        : LP.Name;
                }
            }

            if (Globals::LocalPlayer.Address == 0 ||
                Globals::LocalPlayer.Character.Address == 0) {
                printf("[Cache] Waiting for LocalPlayer/Character...\n");
                continue;
            }

            std::vector<Globals::CachedPlayer> temp_players;
            auto Player_List = GetPlayerList();

            auto localRootInst = Globals::LocalPlayer.Character
                .Find_First_Child("HumanoidRootPart");
            if (localRootInst.Address == 0) {
                std::this_thread::sleep_for(std::chrono::milliseconds(250));
                continue;
            }
            SDK::Part localRootPart(localRootInst.Address);

            for (const auto& P_Inst : Player_List) {
                try {
                    if (P_Inst.Address == 0) continue;
                    if (P_Inst.Address == Globals::LocalPlayer.Address) continue;

                    Globals::CachedPlayer CP;

                    if (!Driver) continue;
                    std::uint64_t charAddr = Driver->Read<std::uint64_t>(
                        P_Inst.Address + Offsets::Player::ModelInstance
                    );
                    CP.Character.Address = charAddr;
                    if (CP.Character.Address == 0) continue;

                    CP.Name = P_Inst.Name.empty()
                        ? SDK::Instance(P_Inst.Address).Name()
                        : P_Inst.Name;

                    auto rootInst = CP.Character.Find_First_Child("HumanoidRootPart");
                    SDK::Part rootPart(rootInst.Address);

                    if (rootPart.Address != 0 && localRootPart.Address != 0) {
                        CP.Distance = rootPart.Get_PartPosition().distance(
                            localRootPart.Get_PartPosition()
                        );
                    }

                    CP.Head = CP.Character.Find_First_Child("Head");
                    CP.Humanoid = CP.Character.Find_First_Child("Humanoid");
                    CP.UpperTorso = CP.Character.Find_First_Child("UpperTorso");
                    CP.LowerTorso = CP.Character.Find_First_Child("LowerTorso");
                    CP.Torso = CP.Character.Find_First_Child("Torso");
                    CP.HumanoidRootPart = rootPart.Address != 0
                        ? SDK::Instance(rootPart.Address) : SDK::Instance();

                    if (CP.Head.Address == 0 || CP.Humanoid.Address == 0) continue;
                    if (CP.Torso.Address == 0 && CP.UpperTorso.Address == 0) continue;

                    bool isR15 = CP.UpperTorso.Address != 0;
                    if (isR15) {
                        CP.LeftUpperArm = CP.Character.Find_First_Child("LeftUpperArm");
                        CP.LeftLowerArm = CP.Character.Find_First_Child("LeftLowerArm");
                        CP.LeftHand = CP.Character.Find_First_Child("LeftHand");
                        CP.RightUpperArm = CP.Character.Find_First_Child("RightUpperArm");
                        CP.RightLowerArm = CP.Character.Find_First_Child("RightLowerArm");
                        CP.RightHand = CP.Character.Find_First_Child("RightHand");
                        CP.LeftUpperLeg = CP.Character.Find_First_Child("LeftUpperLeg");
                        CP.LeftLowerLeg = CP.Character.Find_First_Child("LeftLowerLeg");
                        CP.LeftFoot = CP.Character.Find_First_Child("LeftFoot");
                        CP.RightUpperLeg = CP.Character.Find_First_Child("RightUpperLeg");
                        CP.RightLowerLeg = CP.Character.Find_First_Child("RightLowerLeg");
                        CP.RightFoot = CP.Character.Find_First_Child("RightFoot");
                    }
                    else {
                        CP.LeftArm = CP.Character.Find_First_Child("Left Arm");
                        CP.RightArm = CP.Character.Find_First_Child("Right Arm");
                        CP.LeftLeg = CP.Character.Find_First_Child("Left Leg");
                        CP.RightLeg = CP.Character.Find_First_Child("Right Leg");
                    }

                    try {
                        SDK::Humanoid humanoid(CP.Humanoid.Address);
                        float hp = humanoid.Get_Health();
                        CP.Health = (std::isnan(hp) || hp < 0.f || hp > 1000.f) ? 0.f : hp;
                    }
                    catch (...) { CP.Health = 0.f; }

                    CP.Tool_Name = "None";
                    try {
                        for (const auto& Child : CP.Character.Children()) {
                            if (Child.Address == 0) continue;
                            try {
                                if (Child.Class() == "Tool") {
                                    CP.Tool_Name = Child.Name();
                                    break;
                                }
                            }
                            catch (...) { continue; }
                        }
                    }
                    catch (...) {}

                    temp_players.push_back(CP);
                    printf("[Cache] Added: %s | Head: 0x%llx\n",
                        CP.Name.c_str(), CP.Head.Address);

                }
                catch (...) {
                    printf("[Cache] Exception on player, skipping\n");
                    continue; // ← не крашим, просто пропускаем этого игрока
                }
            }

            size_t count = 0;
            {
                std::lock_guard<std::mutex> lock(Globals::Cache_Mutex);
                Globals::Player_Cache = std::move(temp_players);
                count = Globals::Player_Cache.size();
            }
            printf("[Cache] Total cached: %zu\n", count);
        }
    }

    void RunService() {
        std::thread(CacheUpdateLoop).detach();
    }
}