#include "looting/loot_controller.hpp"

#include <cmath>
#include <iostream>
#include <algorithm>
#include <thread>
#include <chrono>
#include <fstream>
#include <cctype>
#include "common/logger.hpp"
#include "common/math2d.hpp"
#include "common/coordinate_transform.hpp"
#include "common/window_utils.hpp"
#include "common/string_utils.hpp"

namespace {

static std::vector<std::string> ParseJsonStringArray(const std::string& json, const std::string& key) {
    std::vector<std::string> result;
    const std::string searchKey = "\"" + key + "\"";
    size_t keyPos = json.find(searchKey);
    if (keyPos == std::string::npos) return result;

    size_t bracketOpen = json.find('[', keyPos);
    if (bracketOpen == std::string::npos) return result;

    size_t bracketClose = json.find(']', bracketOpen);
    if (bracketClose == std::string::npos) return result;

    size_t curr = bracketOpen + 1;
    while (curr < bracketClose) {
        size_t quoteStart = json.find('"', curr);
        if (quoteStart == std::string::npos || quoteStart >= bracketClose) break;

        size_t quoteEnd = quoteStart + 1;
        while (quoteEnd < bracketClose) {
            if (json[quoteEnd] == '"' && json[quoteEnd - 1] != '\\') {
                break;
            }
            quoteEnd++;
        }
        if (quoteEnd >= bracketClose) break;

        std::string val = json.substr(quoteStart + 1, quoteEnd - quoteStart - 1);
        if (!val.empty()) {
            result.push_back(val);
        }
        curr = quoteEnd + 1;
    }
    return result;
}

static int ParseJsonInt(const std::string& json, const std::string& key, int defaultVal = -1) {
    const std::string searchKey = "\"" + key + "\"";
    size_t keyPos = json.find(searchKey);
    if (keyPos == std::string::npos) return defaultVal;

    size_t colonPos = json.find(':', keyPos);
    if (colonPos == std::string::npos) return defaultVal;

    size_t start = colonPos + 1;
    while (start < json.size() && (json[start] == ' ' || json[start] == '\t' || json[start] == '\r' || json[start] == '\n')) {
        start++;
    }
    if (start >= json.size()) return defaultVal;

    size_t end = start;
    if (json[end] == '-') end++;
    while (end < json.size() && std::isdigit(static_cast<unsigned char>(json[end]))) {
        end++;
    }
    if (end > start) {
        try {
            return std::stoi(json.substr(start, end - start));
        } catch (...) {
            return defaultVal;
        }
    }
    return defaultVal;
}

// Tính điểm ưu tiên nhặt theo bối cảnh POE2: số càng cao ưu tiên càng lớn
int GetItemPriority(const EntityTelemetryData& item) {
    const std::string nameLower = ToLower(item.name);

    // 1. Vàng luôn ưu tiên hàng đầu (Gold vacuum / auto collect)
    if (nameLower.find("gold") != std::string::npos) {
        return 100;
    }
    // 2. Siêu currency
    if (nameLower.find("mirror") != std::string::npos ||
        nameLower.find("divine") != std::string::npos ||
        nameLower.find("perfect jeweller") != std::string::npos) {
        return 99;
    }
    // 3. Currency cao cấp & Quý hiếm POE2
    if (nameLower.find("exalt") != std::string::npos ||
        nameLower.find("chaos") != std::string::npos ||
        nameLower.find("vaal") != std::string::npos ||
        nameLower.find("annul") != std::string::npos ||
        nameLower.find("greater jeweller") != std::string::npos) {
        return 95;
    }
    // 4. Waystones (Endgame Maps POE2 Tier 1-16) & Precursor Tablets
    if (nameLower.find("waystone") != std::string::npos ||
        nameLower.find("tablet") != std::string::npos ||
        nameLower.find("logbook") != std::string::npos) {
        return 92;
    }
    // 5. Uncut Skill/Support/Spirit Gems (Cực kỳ quan trọng trong POE2)
    if (nameLower.find("uncut") != std::string::npos ||
        nameLower.find("gem") != std::string::npos) {
        return 88;
    }
    // 6. Runes & Soul Cores (Vật phẩm khảm nâng cấp trang bị POE2)
    if (nameLower.find("rune") != std::string::npos ||
        nameLower.find("soul core") != std::string::npos ||
        nameLower.find("core") != std::string::npos) {
        return 85;
    }
    // 7. Omens, Distilled Emotions & Essences
    if (nameLower.find("omen") != std::string::npos ||
        nameLower.find("distilled") != std::string::npos ||
        nameLower.find("essence") != std::string::npos) {
        return 82;
    }
    // 8. Currency phổ thông
    if (nameLower.find("orb") != std::string::npos ||
        nameLower.find("scroll") != std::string::npos ||
        nameLower.find("shard") != std::string::npos ||
        nameLower.find("bauble") != std::string::npos ||
        nameLower.find("prism") != std::string::npos ||
        nameLower.find("scrap") != std::string::npos ||
        nameLower.find("whetstone") != std::string::npos) {
        return 80;
    }
    // 9. Unique Items
    if (item.rarity >= 3) {
        return 75;
    }
    // 10. Rare Items
    if (item.rarity == 2) {
        return 65;
    }
    // 11. Charms POE2 (Bùa tự động thay thế Utility Flask)
    if (nameLower.find("charm") != std::string::npos) {
        return 60;
    }
    // 12. Magic Items
    if (item.rarity == 1) {
        return 40;
    }
    // 13. White / Bình thường
    return 10;
}

} // namespace

LootController::LootController(const LootConfig& config)
    : m_config(config) {
}

bool LootController::LoadFilter(const std::string& path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        CoreLog("[LootController] Khong the mo pickup_filter.json tai: " + path);
        return false;
    }
    std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    file.close();

    m_filter.pickupAllAboveRarity = ParseJsonInt(content, "pickup_all_above_rarity", -1);

    auto rawSubs = ParseJsonStringArray(content, "name_substrings");
    m_filter.nameSubstrings.clear();
    for (auto& s : rawSubs) {
        m_filter.nameSubstrings.push_back(ToLower(s));
    }

    auto rawExact = ParseJsonStringArray(content, "exact_types");
    m_filter.exactTypes.clear();
    for (auto& s : rawExact) {
        m_filter.exactTypes.push_back(ToLower(s));
    }

    auto rawRej = ParseJsonStringArray(content, "reject_name_substrings");
    m_filter.rejectNameSubstrings.clear();
    for (auto& s : rawRej) {
        m_filter.rejectNameSubstrings.push_back(ToLower(s));
    }

    m_filter.filterFilePath = path;
    m_filter.isLoaded = true;

    WIN32_FILE_ATTRIBUTE_DATA fileData{};
    if (GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &fileData)) {
        m_filter.lastFileModTime = (static_cast<uint64_t>(fileData.ftLastWriteTime.dwHighDateTime) << 32) |
                                   fileData.ftLastWriteTime.dwLowDateTime;
    }

    std::string msg = "[LootController] Da nap PickupFilter tu '" + path + "': " +
                      std::to_string(m_filter.exactTypes.size()) + " exact types, " +
                      std::to_string(m_filter.nameSubstrings.size()) + " substrings, " +
                      std::to_string(m_filter.rejectNameSubstrings.size()) + " rejects, minRarity=" +
                      std::to_string(m_filter.pickupAllAboveRarity);
    std::cout << msg << std::endl;
    CoreLog(msg);
    return true;
}

void LootController::CheckReloadFilter(uint64_t nowMs) {
    if (m_filter.filterFilePath.empty()) return;
    if (nowMs - m_filter.lastCheckMs < 1000) return; // Kiểm tra định kỳ mỗi 1000ms
    m_filter.lastCheckMs = nowMs;

    WIN32_FILE_ATTRIBUTE_DATA fileData{};
    if (GetFileAttributesExA(m_filter.filterFilePath.c_str(), GetFileExInfoStandard, &fileData)) {
        uint64_t modTime = (static_cast<uint64_t>(fileData.ftLastWriteTime.dwHighDateTime) << 32) |
                           fileData.ftLastWriteTime.dwLowDateTime;
        if (modTime != m_filter.lastFileModTime) {
            LoadFilter(m_filter.filterFilePath);
        }
    }
}

int LootController::GetPriority(const EntityTelemetryData& item) const {
    if (m_filter.isLoaded) {
        const std::string nameLower = ToLower(item.name);
        for (const auto& rej : m_filter.rejectNameSubstrings) {
            if (!rej.empty() && nameLower.find(rej) != std::string::npos) {
                return 0; // Đồ bị filter reject/ẩn có priority = 0
            }
        }
    }
    return GetItemPriority(item);
}

void LootController::ResetAreaState() {
    m_state.ignoredOrLootedIds.clear();
    m_state.lootedHistory.clear();
}

bool LootController::ShouldLoot(const EntityTelemetryData& item) const {
    if (item.type != 2) return false; // Chỉ nhặt Type = 2 (EntityTypeItemDrop)

    const std::string nameLower = ToLower(item.name);

    // Bất biến INV-LOOT-PROXIMITY-22U: Khi túi đồ đầy, chỉ cho phép nhặt đồ 1 slot priority >= 80
    // (Vàng, Currency, Waystones, Uncut Gems, Runes), từ chối toàn bộ trang bị vũ khí/áo giáp cồng kềnh.
    if (m_config.isInventoryFull) {
        if (GetPriority(item) < 80) {
            return false;
        }
        if (nameLower.find("armour") != std::string::npos ||
            nameLower.find("cuirass") != std::string::npos ||
            nameLower.find("doublet") != std::string::npos ||
            nameLower.find("plate") != std::string::npos ||
            nameLower.find("garb") != std::string::npos ||
            nameLower.find("robe") != std::string::npos ||
            nameLower.find("vest") != std::string::npos ||
            nameLower.find("coat") != std::string::npos ||
            nameLower.find("boots") != std::string::npos ||
            nameLower.find("gloves") != std::string::npos ||
            nameLower.find("helmet") != std::string::npos ||
            nameLower.find("shield") != std::string::npos ||
            nameLower.find("staff") != std::string::npos ||
            nameLower.find("bow") != std::string::npos ||
            nameLower.find("sword") != std::string::npos ||
            nameLower.find("axe") != std::string::npos ||
            nameLower.find("mace") != std::string::npos ||
            nameLower.find("quarterstaff") != std::string::npos ||
            nameLower.find("crossbow") != std::string::npos) {
            return false;
        }
    }

    if (m_filter.isLoaded) {
        // 1. REJECT LIST: Bất kỳ item nào nằm trong reject_name_substrings -> CẤM NHẶT (bị filter ẩn đi)
        for (const auto& rej : m_filter.rejectNameSubstrings) {
            if (!rej.empty() && nameLower.find(rej) != std::string::npos) {
                return false;
            }
        }

        // 2. EXACT TYPES: Khớp chính xác tên item được chỉ định trong filter
        for (const auto& ex : m_filter.exactTypes) {
            if (!ex.empty() && nameLower == ex) {
                return true;
            }
        }

        // 3. Lọc theo Rarity tối thiểu của Filter
        if (m_filter.pickupAllAboveRarity >= 0 && item.rarity >= m_filter.pickupAllAboveRarity) {
            return true;
        }

        // 4. SUBSTRINGS: Khớp các từ khóa tiền tệ, waystone, gem, v.v. được filter cho phép
        for (const auto& sub : m_filter.nameSubstrings) {
            if (!sub.empty() && nameLower.find(sub) != std::string::npos) {
                return true;
            }
        }

        // 5. User Invariant: Không khớp bất kỳ điều kiện nào của filter -> Đồ bị filter ẩn đi -> TUYỆT ĐỐI CẤM NHẶT!
        return false;
    }

    // FALLBACK khi chưa nạp filter: Giữ hành vi an toàn mặc định
    // 1. Vàng
    if (m_config.pickGold && nameLower.find("gold") != std::string::npos) {
        return true;
    }

    // 2. Waystones (Endgame Maps)
    if (m_config.pickWaystones && (nameLower.find("waystone") != std::string::npos ||
                                   nameLower.find("tablet") != std::string::npos ||
                                   nameLower.find("logbook") != std::string::npos)) {
        return true;
    }

    // 3. Uncut Gems (Skill/Support/Spirit Gems)
    if (m_config.pickUncutGems && (nameLower.find("uncut") != std::string::npos ||
                                   nameLower.find("gem") != std::string::npos)) {
        return true;
    }

    // 4. Runes & Soul Cores
    if (m_config.pickRunesAndCores && (nameLower.find("rune") != std::string::npos ||
                                       nameLower.find("soul core") != std::string::npos ||
                                       nameLower.find("core") != std::string::npos)) {
        return true;
    }

    // 5. Charms
    if (m_config.pickCharms && nameLower.find("charm") != std::string::npos) {
        return true;
    }

    // 6. Currency & Tiền tệ
    if (m_config.pickCurrency) {
        if (nameLower.find("orb") != std::string::npos ||
            nameLower.find("scroll") != std::string::npos ||
            nameLower.find("divine") != std::string::npos ||
            nameLower.find("mirror") != std::string::npos ||
            nameLower.find("exalt") != std::string::npos ||
            nameLower.find("chaos") != std::string::npos ||
            nameLower.find("shard") != std::string::npos ||
            nameLower.find("essence") != std::string::npos ||
            nameLower.find("omen") != std::string::npos ||
            nameLower.find("distilled") != std::string::npos ||
            nameLower.find("bauble") != std::string::npos ||
            nameLower.find("prism") != std::string::npos ||
            nameLower.find("scrap") != std::string::npos ||
            nameLower.find("whetstone") != std::string::npos ||
            nameLower.find("key") != std::string::npos) {
            return true;
        }
    }

    // 7. Lọc theo Rarity trang bị
    if (item.rarity >= m_config.minEquipmentRarity) {
        return true;
    }

    // 8. Tận dụng cờ hide_all_filtered_ground_items=true của POE2
    if (m_config.respectGameFilter && m_config.hideFilteredGroundItems) {
        if (item.rarity > 0) return true;
    }

    return false;
}

ActionProposal LootController::Propose(const TelemetryPacket& packet, uint64_t nowMs) {
    if (!m_config.enabled) return {};
    CheckReloadFilter(nowMs);
    const auto& player = packet.player;
    if (player.maxHP > 0 && player.currentHP == 0) return {};

    // Bất biến INV-SAFE-01: Có quái sống trong cự ly safe_range -> cấm Pickup (trả về proposal rỗng priority = 0)
    for (uint32_t i = 0; i < packet.entityCount; ++i) {
        const auto& ent = packet.entities[i];
        if (ent.type == 1 && !(ent.extraFlags & 4) && ent.maxHP > 0) {
            const float dx = ent.posX - player.posX;
            const float dy = ent.posY - player.posY;
            const float dist = std::sqrt(dx * dx + dy * dy);
            if (dist <= m_config.safeRange) {
                return {};
            }
        }
    }

    // Dọn dẹp cache ID đã nhặt sau 15 giây nếu không còn tồn tại trên radar
    while (!m_state.lootedHistory.empty() && nowMs - m_state.lootedHistory.front().second > 15000) {
        m_state.ignoredOrLootedIds.erase(m_state.lootedHistory.front().first);
        m_state.lootedHistory.erase(m_state.lootedHistory.begin());
    }

    // Tìm vật phẩm tốt nhất trong bán kính
    int bestPriority = -1;
    float bestDistance = 9999.0f;
    int bestIndex = -1;

    for (uint32_t i = 0; i < packet.entityCount; ++i) {
        const auto& ent = packet.entities[i];
        if (ent.type != 2) continue; // Phải là ItemDrop
        if (m_state.ignoredOrLootedIds.contains(ent.id)) continue;

        const float dx = ent.posX - player.posX;
        const float dy = ent.posY - player.posY;
        const float dist = std::sqrt(dx * dx + dy * dy);

        if (dist <= m_config.lootRadius && ShouldLoot(ent)) {
            const int prio = GetItemPriority(ent);
            if (prio > bestPriority || (prio == bestPriority && dist < bestDistance)) {
                bestPriority = prio;
                bestDistance = dist;
                bestIndex = static_cast<int>(i);
            }
        }
    }

    if (bestIndex >= 0) {
        const auto& targetItem = packet.entities[bestIndex];
        ActionProposal p{};
        p.kind = BotActionKind::Pickup;
        p.priority = bot_priority::PICKUP; // Luôn 6000 theo chuẩn Doc 29
        p.priorityName = "PICKUP";
        p.entityId = targetItem.id;
        p.targetX = targetItem.posX;
        p.targetY = targetItem.posY;
        p.targetZ = targetItem.posZ;
        p.distance = bestDistance;
        return p;
    }

    return {};
}

bool LootController::Execute(const ActionProposal& chosen, const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (chosen.kind != BotActionKind::Pickup) return false;
    if (!EnsurePoe2WindowFocus()) return false;
    if (nowMs - m_state.lastLootMs < m_config.lootIntervalMs) return false;

    for (uint32_t i = 0; i < packet.entityCount; ++i) {
        if (packet.entities[i].id == chosen.entityId) {
            const auto& item = packet.entities[i];
            const float dx = item.posX - packet.player.posX;
            const float dy = item.posY - packet.player.posY;
            const float dist = std::sqrt(dx * dx + dy * dy);

            // Walk-To-Loot (GAP-5): Nếu đồ ở xa ngoài tầm nhặt (>22u) và đang dùng WASD
            if (m_config.moveMode == MovementMode::WASD && dist > m_config.pickupRange) {
                common::WasdVector wasd = common::WorldToWasd(dx, dy);
                if (!wasd.empty()) {
                    std::vector<uint8_t> moveKeys;
                    if (wasd.up) moveKeys.push_back(0x1A);
                    if (wasd.left) moveKeys.push_back(0x04);
                    if (wasd.down) moveKeys.push_back(0x16);
                    if (wasd.right) moveKeys.push_back(0x07);
                    HoldLootWasd(moveKeys, kmbox);
                    m_state.lastLootMs = nowMs;
                    return true;
                }
            }

            ReleaseLootWasd(kmbox);
            ExecutePickup(item, packet.player, kmbox, nowMs);
            return true;
        }
    }
    return false;
}

bool LootController::Update(const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    ActionProposal p = Propose(packet, nowMs);
    if (p.priority > 0) {
        return Execute(p, packet, kmbox, nowMs);
    }

    // Fallback Proximity Sweep: Alt (nhãn đồ) hoặc 'V' gom đồ gần nhất.
    // INV-WASD-NO-BLIND-LMB: LMB là skill; tương tác nhặt = F, không click đất.
    if ((GetAsyncKeyState(VK_MENU) & 0x8000) || (GetAsyncKeyState('V') & 0x8000)) {
        if (!EnsurePoe2WindowFocus()) return false;
        m_state.lastLootMs = nowMs;
        ++m_state.totalItemsLooted;
        kmbox.KeyPress(0x09, m_config.ultraFast ? 3 : 35);
        return true;
    }

    return false;
}

void LootController::ExecutePickup(const EntityTelemetryData& item, const PlayerTelemetryData& player, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!EnsurePoe2WindowFocus()) return;
    const auto startTime = std::chrono::high_resolution_clock::now();

    m_state.lastLootMs = nowMs;
    m_state.lastLootedEntityId = item.id;
    m_state.ignoredOrLootedIds.insert(item.id);
    m_state.lootedHistory.push_back({ item.id, nowMs });
    ++m_state.totalItemsLooted;

    const std::string nameLower = ToLower(item.name);
    if (nameLower.find("gold") != std::string::npos) {
        ++m_state.totalGoldLooted; // Đếm số lượng đống vàng đã nhặt thực tế, loại bỏ số ảo hardcode +150
    }

    // Tọa độ màn hình từ config nạp động (mặc định 2560x1440)
    int screenW = (m_config.screenWidth > 0) ? m_config.screenWidth : GetSystemMetrics(SM_CXSCREEN);
    int screenH = (m_config.screenHeight > 0) ? m_config.screenHeight : GetSystemMetrics(SM_CYSCREEN);
    if (screenW <= 0) screenW = 2560;
    if (screenH <= 0) screenH = 1440;

    const float dx = item.posX - player.posX;
    const float dy = item.posY - player.posY;
    const float dist = std::sqrt(dx * dx + dy * dy);

    const float clickRad = (dist > 0.5f) ? (std::min)(350.0f, (std::max)(60.0f, dist * 12.0f)) : 0.0f;
    const auto target = common::WorldToScreenIsometric(dx, dy, clickRad, static_cast<float>(screenW), static_cast<float>(screenH), 50.0f);
    kmbox.MoveMouseSmooth(static_cast<int>(target.x), static_cast<int>(target.y), m_curveGen,
                          m_config.ultraFast ? 1 : 10, m_config.ultraFast ? 0 : 1);
    kmbox.KeyPress(0x09, m_config.ultraFast ? 3 : 35);

    const auto endTime = std::chrono::high_resolution_clock::now();
    m_state.lastPickupDurationMs = static_cast<uint64_t>(
        std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count());

    // Log nhặt đồ
    const std::string logMsg = "[AutoLoot #" + std::to_string(m_state.totalItemsLooted) + "] Nhat: "
        + item.name + " (Rarity: " + std::to_string(item.rarity)
        + ", Dist: " + std::to_string(static_cast<int>(item.distanceToPlayer))
        + ", Latency: " + std::to_string(m_state.lastPickupDurationMs) + "ms)";
    std::cout << logMsg << std::endl;
    CoreLog(logMsg);
}

void LootController::ReleaseLootWasd(KMBoxNet& kmbox) {
    for (uint8_t hid : m_heldLootHids) {
        kmbox.KeyUp(hid);
    }
    m_heldLootHids.clear();
}

void LootController::HoldLootWasd(const std::vector<uint8_t>& hids, KMBoxNet& kmbox) {
    for (uint8_t held : m_heldLootHids) {
        bool keep = false;
        for (uint8_t next : hids) {
            if (next == held) {
                keep = true;
                break;
            }
        }
        if (!keep) {
            kmbox.KeyUp(held);
        }
    }
    for (uint8_t next : hids) {
        bool already = false;
        for (uint8_t held : m_heldLootHids) {
            if (held == next) {
                already = true;
                break;
            }
        }
        if (!already) {
            kmbox.KeyDown(next);
        }
    }
    m_heldLootHids = hids;
}
