#include "brain/inventory_handler.hpp"

#include <fstream>
#include <iostream>
#include <sstream>
#include <cmath>
#include "common/logger.hpp"
#include "common/window_utils.hpp"
#include "common/toml_parser.hpp"

namespace {

using common::ParseTomlFloat;
using common::ParseTomlInt;
using common::ParseTomlUint;

static bool EnsureWindowFocus() {
    if (KMBoxNet::IsAnyIgnoreWindowFocus()) return true;
    HWND hwnd = common::FindPoe2Window();
    if (!hwnd) return false;
    return (GetForegroundWindow() == hwnd);
}

} // namespace

InventoryHandler::InventoryHandler() {
    m_activeCmd.timeoutMs = 15000;
}

bool InventoryHandler::LoadCoordinates(const std::string& tomlPath) {
    std::ifstream file(tomlPath);
    if (!file.is_open()) {
        CoreLog("[InventoryHandler] Khong the mo coordinates.toml tai: " + tomlPath);
        return false;
    }

    std::string line;
    std::string currentSection = "";
    while (std::getline(file, line)) {
        // Strip comment
        size_t cPos = line.find('#');
        if (cPos != std::string::npos) line = line.substr(0, cPos);

        // Trim leading whitespace
        size_t start = line.find_first_not_of(" \t\r\n");
        if (start == std::string::npos) continue;
        line = line.substr(start);

        // Section header
        if (line.front() == '[' && line.find(']') != std::string::npos) {
            size_t end = line.find(']');
            currentSection = line.substr(1, end - 1);
            continue;
        }

        if (currentSection == "screen") {
            ParseTomlFloat(line, "reference_width", m_coords.refWidth);
            ParseTomlFloat(line, "reference_height", m_coords.refHeight);
        } else if (currentSection == "inventory") {
            ParseTomlFloat(line, "origin_x", m_coords.invOriginX);
            ParseTomlFloat(line, "origin_y", m_coords.invOriginY);
            ParseTomlFloat(line, "cell_width", m_coords.invCellW);
            ParseTomlFloat(line, "cell_height", m_coords.invCellH);
            ParseTomlFloat(line, "offset_x", m_coords.invOffsetX);
            ParseTomlFloat(line, "offset_y", m_coords.invOffsetY);
            ParseTomlInt(line, "cols", m_coords.invCols);
            ParseTomlInt(line, "rows", m_coords.invRows);
        } else if (currentSection == "stash") {
            ParseTomlFloat(line, "origin_x", m_coords.stashOriginX);
            ParseTomlFloat(line, "origin_y", m_coords.stashOriginY);
            ParseTomlFloat(line, "cell_width", m_coords.stashCellW);
            ParseTomlFloat(line, "cell_height", m_coords.stashCellH);
            ParseTomlFloat(line, "offset_x", m_coords.stashOffsetX);
            ParseTomlFloat(line, "offset_y", m_coords.stashOffsetY);
            ParseTomlInt(line, "cols", m_coords.stashCols);
            ParseTomlInt(line, "rows", m_coords.stashRows);
            ParseTomlFloat(line, "tab_start_x", m_coords.tabStartX);
            ParseTomlFloat(line, "tab_start_y", m_coords.tabStartY);
            ParseTomlFloat(line, "tab_spacing_x", m_coords.tabSpacingX);
            ParseTomlInt(line, "tab_count", m_coords.tabCount);
        } else if (currentSection == "vendor") {
            ParseTomlFloat(line, "sell_tab_x", m_coords.vendorSellTabX);
            ParseTomlFloat(line, "sell_tab_y", m_coords.vendorSellTabY);
            ParseTomlFloat(line, "accept_button_x", m_coords.vendorAcceptX);
            ParseTomlFloat(line, "accept_button_y", m_coords.vendorAcceptY);
            ParseTomlFloat(line, "close_button_x", m_coords.closeButtonX);
            ParseTomlFloat(line, "close_button_y", m_coords.closeButtonY);
        } else if (currentSection == "delays") {
            ParseTomlUint(line, "default_step_delay_ms", m_coords.stepDelayMs);
            ParseTomlUint(line, "item_transfer_delay_ms", m_coords.itemTransferDelayMs);
            ParseTomlUint(line, "jitter_ms", m_coords.jitterMs);
        }
    }

    std::string msg = "[InventoryHandler] Da nap coordinates tu '" + tomlPath + "' (Ref: " +
                      std::to_string(static_cast<int>(m_coords.refWidth)) + "x" +
                      std::to_string(static_cast<int>(m_coords.refHeight)) + ")";
    std::cout << msg << std::endl;
    CoreLog(msg);
    return true;
}

bool InventoryHandler::IsSafeTownZone(const TelemetryPacket& packet) const {
    // Bất biến INV-TOWN-01: Nếu có bất kỳ quái vật sống nào xung quanh -> Nguy hiểm, không phải Town/Hideout an toàn
    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) {
            return false;
        }
    }
    return true;
}

bool InventoryHandler::StartStash(const CommandPacket& cmd, uint64_t nowMs) {
    m_activeCmd = cmd;
    if (m_activeCmd.timeoutMs == 0) m_activeCmd.timeoutMs = 15000;

    m_routineStartMs = nowMs;
    m_lastStepMs = nowMs;
    m_stashState = StashFsmState::OpenPanels;
    m_vendorState = VendorFsmState::Idle;
    m_currentCol = 0;
    m_currentRow = m_reserveRow0 ? 1 : 0; // Bất biến INV-INVENTORY-ROW0-RESERVED
    m_itemsTransferred = 0;
    m_targetTab = cmd.targetEntityId;

    std::string msg = "[InventoryHandler] Start STASH routine (Cmd #" + std::to_string(cmd.commandId) +
                      ", Tab=" + std::to_string(m_targetTab) + ", StartRow=" + std::to_string(m_currentRow) +
                      ", Timeout=" + std::to_string(m_activeCmd.timeoutMs) + "ms)";
    std::cout << msg << std::endl;
    CoreLog(msg);
    return true;
}

bool InventoryHandler::StartVendor(const CommandPacket& cmd, uint64_t nowMs) {
    m_activeCmd = cmd;
    if (m_activeCmd.timeoutMs == 0) m_activeCmd.timeoutMs = 15000;

    m_routineStartMs = nowMs;
    m_lastStepMs = nowMs;
    m_vendorState = VendorFsmState::ApproachNpc;
    m_stashState = StashFsmState::Idle;
    m_currentCol = 0;
    m_currentRow = m_reserveRow0 ? 1 : 0; // Bất biến INV-INVENTORY-ROW0-RESERVED

    std::string msg = "[InventoryHandler] Start VENDOR routine (Cmd #" + std::to_string(cmd.commandId) +
                      ", Timeout=" + std::to_string(m_activeCmd.timeoutMs) + "ms)";
    std::cout << msg << std::endl;
    CoreLog(msg);
    return true;
}

void InventoryHandler::Reset() {
    m_stashState = StashFsmState::Idle;
    m_vendorState = VendorFsmState::Idle;
    m_itemsTransferred = 0;
    m_routineStartMs = 0;
}

ActionProposal InventoryHandler::Propose(const TelemetryPacket& packet, uint64_t nowMs) {
    // 1. Kiểm tra timeout
    if (m_routineStartMs > 0 && (nowMs - m_routineStartMs > m_activeCmd.timeoutMs)) {
        if (m_stashState != StashFsmState::Idle && m_stashState != StashFsmState::Completed && m_stashState != StashFsmState::Failed) {
            m_stashState = StashFsmState::Failed;
            CoreLog("[InventoryHandler] Stash routine TIMEOUT (" + std::to_string(m_activeCmd.timeoutMs) + "ms)!");
        }
        if (m_vendorState != VendorFsmState::Idle && m_vendorState != VendorFsmState::Completed && m_vendorState != VendorFsmState::Failed) {
            m_vendorState = VendorFsmState::Failed;
            CoreLog("[InventoryHandler] Vendor routine TIMEOUT (" + std::to_string(m_activeCmd.timeoutMs) + "ms)!");
        }
    }

    // 2. Bất biến INV-TOWN-01: Nếu đang chạy routine mà phát hiện quái vật sống -> Hủy khẩn cấp (Fail-closed)
    if (IsBusy() && !IsSafeTownZone(packet)) {
        m_stashState = StashFsmState::Failed;
        m_vendorState = VendorFsmState::Failed;
        CoreLog("[InventoryHandler] INV-TOWN-01 Violated: Live monster detected in area! Aborting routine.");
        return {};
    }

    // 3. Đề xuất hành động tương ứng với FSM đang chạy
    if (m_stashState != StashFsmState::Idle && m_stashState != StashFsmState::Completed && m_stashState != StashFsmState::Failed) {
        ActionProposal p{};
        p.kind = BotActionKind::Stash;
        p.priority = bot_priority::STASH; // 3500
        p.priorityName = "STASH";
        p.entityId = m_activeCmd.targetEntityId;
        return p;
    }

    if (m_vendorState != VendorFsmState::Idle && m_vendorState != VendorFsmState::Completed && m_vendorState != VendorFsmState::Failed) {
        ActionProposal p{};
        p.kind = BotActionKind::Vendor;
        p.priority = bot_priority::VENDOR; // 3400
        p.priorityName = "VENDOR";
        p.entityId = m_activeCmd.targetEntityId;
        return p;
    }

    return {};
}

bool InventoryHandler::Execute(const ActionProposal& chosen, const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (chosen.kind == BotActionKind::Stash) {
        return AdvanceStashFsm(packet, kmbox, nowMs);
    }
    if (chosen.kind == BotActionKind::Vendor) {
        return AdvanceVendorFsm(packet, kmbox, nowMs);
    }
    return false;
}

bool InventoryHandler::AdvanceStashFsm(const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!EnsureWindowFocus()) return false;

    // Kiểm tra độ trễ giữa các bước FSM
    uint32_t delay = (m_stashState == StashFsmState::DepositItems) ? m_coords.itemTransferDelayMs : m_coords.stepDelayMs;
    if (nowMs - m_lastStepMs < delay) {
        return true; // Vẫn đang trong bước hiện tại
    }

    int screenW = 0;
    int screenH = 0;
    common::GetGameResolution(screenW, screenH);

    switch (m_stashState) {
        case StashFsmState::OpenPanels: {
            // Bước 1: Mở túi đồ (phím I) và tương tác hòm đồ
            kmbox.KeyPress('I', 30);
            m_stashState = StashFsmState::SelectTab;
            m_lastStepMs = nowMs;
            CoreLog("[InventoryHandler] FSM: OpenPanels -> SelectTab");
            break;
        }
        case StashFsmState::SelectTab: {
            // Bước 2: Nhấp chọn Tab hòm đồ
            UiPoint tabPt = m_coords.GetStashTab(m_targetTab, static_cast<float>(screenW), static_cast<float>(screenH));
            kmbox.MoveMouseSmooth(static_cast<int>(tabPt.x), static_cast<int>(tabPt.y), m_curveGen, 10, 1);
            kmbox.ClickMouse(1, 15);
            m_stashState = StashFsmState::DepositItems;
            m_lastStepMs = nowMs;
            CoreLog("[InventoryHandler] FSM: SelectTab -> DepositItems (Tab " + std::to_string(m_targetTab) + ")");
            break;
        }
        case StashFsmState::DepositItems: {
            // Bước 3: Di chuyển chuột vào ô túi đồ và Ctrl + Click
            UiPoint cellPt = m_coords.GetInventoryCell(m_currentCol, m_currentRow, static_cast<float>(screenW), static_cast<float>(screenH));
            kmbox.MoveMouseSmooth(static_cast<int>(cellPt.x), static_cast<int>(cellPt.y), m_curveGen, 8, 1);

            // Ctrl + Click để chuyển đồ siêu tốc vào Stash
            kmbox.KeyDown(VK_CONTROL);
            kmbox.ClickMouse(1, 12);
            kmbox.KeyUp(VK_CONTROL);

            ++m_itemsTransferred;
            ++m_currentCol;
            if (m_currentCol >= m_coords.invCols) {
                m_currentCol = 0;
                ++m_currentRow;
            }

            m_lastStepMs = nowMs;
            if (m_itemsTransferred >= static_cast<uint32_t>(m_maxItemsToTransfer) || m_currentRow >= m_coords.invRows) {
                m_stashState = StashFsmState::ClosePanels;
                CoreLog("[InventoryHandler] FSM: DepositItems -> ClosePanels (Transferred: " + std::to_string(m_itemsTransferred) + ")");
            }
            break;
        }
        case StashFsmState::ClosePanels: {
            // Bước 4: Đóng toàn bộ giao diện panel (phím Space hoặc Esc)
            kmbox.KeyPress(VK_SPACE, 30);
            m_stashState = StashFsmState::Completed;
            m_lastStepMs = nowMs;
            CoreLog("[InventoryHandler] FSM: ClosePanels -> Completed.");
            std::cout << "[InventoryHandler] >> Quy trình Stash hoàn tất (" << m_itemsTransferred << " items đã chuyển) <<" << std::endl;
            break;
        }
        case StashFsmState::Completed:
        case StashFsmState::Failed:
        case StashFsmState::Idle:
            break;
    }
    return true;
}

bool InventoryHandler::AdvanceVendorFsm(const TelemetryPacket& packet, KMBoxNet& kmbox, uint64_t nowMs) {
    if (!EnsureWindowFocus()) return false;

    if (nowMs - m_lastStepMs < m_coords.stepDelayMs) {
        return true;
    }

    int screenW = 0;
    int screenH = 0;
    common::GetGameResolution(screenW, screenH);

    switch (m_vendorState) {
        case VendorFsmState::ApproachNpc: {
            // Mở panel bán đồ
            kmbox.KeyPress('I', 30);
            m_vendorState = VendorFsmState::OpenVendor;
            m_lastStepMs = nowMs;
            CoreLog("[InventoryHandler] Vendor FSM: ApproachNpc -> OpenVendor");
            break;
        }
        case VendorFsmState::OpenVendor: {
            // Nhấp chọn tab bán đồ
            UiPoint sellTab = m_coords.ScalePoint(m_coords.vendorSellTabX, m_coords.vendorSellTabY, static_cast<float>(screenW), static_cast<float>(screenH));
            kmbox.MoveMouseSmooth(static_cast<int>(sellTab.x), static_cast<int>(sellTab.y), m_curveGen, 10, 1);
            kmbox.ClickMouse(1, 15);
            m_vendorState = VendorFsmState::SellOrSalvage;
            m_lastStepMs = nowMs;
            CoreLog("[InventoryHandler] Vendor FSM: OpenVendor -> SellOrSalvage");
            break;
        }
        case VendorFsmState::SellOrSalvage: {
            // Nhấp nút chấp nhận bán (Accept offer)
            UiPoint acceptPt = m_coords.ScalePoint(m_coords.vendorAcceptX, m_coords.vendorAcceptY, static_cast<float>(screenW), static_cast<float>(screenH));
            kmbox.MoveMouseSmooth(static_cast<int>(acceptPt.x), static_cast<int>(acceptPt.y), m_curveGen, 10, 1);
            kmbox.ClickMouse(1, 15);
            m_vendorState = VendorFsmState::ClosePanels;
            m_lastStepMs = nowMs;
            CoreLog("[InventoryHandler] Vendor FSM: SellOrSalvage -> ClosePanels");
            break;
        }
        case VendorFsmState::ClosePanels: {
            kmbox.KeyPress(VK_SPACE, 30);
            m_vendorState = VendorFsmState::Completed;
            m_lastStepMs = nowMs;
            CoreLog("[InventoryHandler] Vendor FSM: ClosePanels -> Completed.");
            std::cout << "[InventoryHandler] >> Quy trình Vendor hoàn tất <<" << std::endl;
            break;
        }
        case VendorFsmState::Completed:
        case VendorFsmState::Failed:
        case VendorFsmState::Idle:
            break;
    }
    return true;
}
