#include "brain/character_fsm.hpp"
#include "brain/map_device_handler.hpp"
#include "brain/inventory_handler.hpp"
#include "brain/npc_interaction_handler.hpp"
#include "common/logger.hpp"
#include <cstring>
#include <cmath>
#include <iostream>

namespace {
bool IsInMapState(CharacterState state) {
    return state == CharacterState::InMapExploring ||
           state == CharacterState::InMapCombat ||
           state == CharacterState::InMapLooting ||
           state == CharacterState::InMapPortalExit;
}
} // namespace

using common::ZoneKind;

bool CharacterFSM::CheckMapCompleted(const TelemetryPacket& packet, uint64_t nowMs) const {
    if (m_forceMapCompleted) return true;

    // Hard safety timeout: 6 phút trong map (kMapMaxDurationMs)
    if (m_mapEnterMs > 0 && (nowMs - m_mapEnterMs >= kMapMaxDurationMs)) {
        return true;
    }

    // Tiêu chí 1: Boss đã bị tiêu diệt VÀ không còn quái nguy hiểm (Rare/Magic) trong cự ly 450u
    if (m_bossSlain) {
        bool dangerousMonsterNear = false;
        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 && ent.rarity >= 1) {
                const float dx = ent.posX - packet.player.posX;
                const float dy = ent.posY - packet.player.posY;
                if (std::sqrt(dx * dx + dy * dy) <= 450.0f) {
                    dangerousMonsterNear = true;
                    break;
                }
            }
        }
        if (!dangerousMonsterNear) {
            return true;
        }
    }

    // Tiêu chí 2: Độ bao phủ Heatmap đạt >= 85% diện tích map
    if (m_heatmapCoverage >= 0.85f) {
        return true;
    }

    // Tiêu chí 3: Yên ắng vắng quái hợp lệ (INV-MAP-LEGITIMATE-CLEAR-V2)
    // CHỈ ĐƯỢC KẾT LUẬN HOÀN TẤT KHI ĐÃ KHÁM PHÁ ĐỦ BẢN ĐỒ (Coverage >= 80% hoặc Quãng đường >= 1200u)
    // VÀ thời lượng trong map >= kMapMinDurationMs (90s) VÀ đã yên ắng liên tục kMapLegitimateQuietMs (30s)
    // Triệt tiêu 100% tình trạng đánh 1 con quái hoặc vào góc vắng 30s rồi kết luận map clear!
    const bool hasExploredEnough = (m_heatmapCoverage >= kMinCoverageRatio) ||
                                  (m_distanceTraversed >= kMinTraverseUnits);
    if (hasExploredEnough && m_mapEnterMs > 0 && (nowMs - m_mapEnterMs >= kMapMinDurationMs)) {
        if (m_mapQuietStartMs > 0 && (nowMs - m_mapQuietStartMs >= kMapLegitimateQuietMs)) {
            return true;
        }
    }

    return false;
}

void CharacterFSM::TransitionTo(CharacterState newState, uint64_t nowMs) {
    if (m_state == newState) return;
    const CharacterState oldState = m_state;
    m_state = newState;
    m_stateEnterMs = nowMs;
    m_mapQuietStartMs = (newState == CharacterState::InMapExploring) ? nowMs : 0;

    if (newState == CharacterState::InMapExploring && m_mapEnterMs == 0) {
        m_mapEnterMs = nowMs;
    }

    if ((newState == CharacterState::HideoutIdle || newState == CharacterState::TownIdle)) {
        if (IsInMapState(oldState)) {
            m_pendingStashAfterMap = true;
            m_hideoutEnterMs = nowMs;
        }
        m_mapEnterMs = 0;
        m_bossSlain = false;
        m_heatmapCoverage = 0.0f;
        m_forceMapCompleted = false;
        m_distanceTraversed = 0.0f;
        m_lastPosX = 0.0f;
        m_lastPosY = 0.0f;
    }
    if (newState == CharacterState::HideoutMapDeviceApproach) {
        m_pendingStashAfterMap = false;
    }

    CoreLog(std::string("[CharacterFSM] Transition: ") +
            CharacterStateToString(oldState) + " -> " +
            CharacterStateToString(newState) + " (Timestamp=" + std::to_string(nowMs) + "ms)");
}

void CharacterFSM::ForceState(CharacterState state, uint64_t nowMs) {
    TransitionTo(state, nowMs);
}

void CharacterFSM::Reset() {
    m_state = CharacterState::Uninitialized;
    m_lastAreaSeed = 0;
    m_stateEnterMs = 0;
    m_lastLogMs = 0;
    m_lastLoggedState = CharacterState::Uninitialized;
    m_lastMapDeviceFailMs = 0;
    m_pendingStashAfterMap = false;
    m_hideoutEnterMs = 0;
    m_mapEnterMs = 0;
    m_mapQuietStartMs = 0;
    m_bossSlain = false;
    m_heatmapCoverage = 0.0f;
    m_forceMapCompleted = false;
    m_distanceTraversed = 0.0f;
    m_lastPosX = 0.0f;
    m_lastPosY = 0.0f;
}

bool CharacterFSM::TriggerMapDeviceRoutine(uint64_t nowMs) {
    if (m_mapDeviceHandler == nullptr) return false;
    if (m_state != CharacterState::HideoutIdle) return false;
    if (m_lastMapDeviceFailMs != 0 && (nowMs - m_lastMapDeviceFailMs < kMapDeviceRetryCooldownMs)) return false;

    CoreLog("[CharacterFSM] Kích hoạt chu trình MAP_DEVICE tại Hideout (INV-FSM-HIDEOUT-F8-MAP-LOOP).");
    m_mapDeviceHandler->StartMapDevice(0, 0, false, nowMs);
    TransitionTo(CharacterState::HideoutMapDeviceApproach, nowMs);
    return true;
}

void CharacterFSM::Update(const TelemetryPacket& packet, uint64_t nowMs) {
    const ZoneKind currentZone = common::ClassifyZone(packet.area);

    // 0. Cập nhật quãng đường tuần tra tích lũy khi ở trong Map (INV-MAP-LEGITIMATE-CLEAR-V2)
    if (IsInMapState(m_state)) {
        if (m_lastPosX != 0.0f || m_lastPosY != 0.0f) {
            const float stepDx = packet.player.posX - m_lastPosX;
            const float stepDy = packet.player.posY - m_lastPosY;
            const float stepDist = std::sqrt(stepDx * stepDx + stepDy * stepDy);
            if (stepDist >= 0.5f && stepDist <= 250.0f) {
                m_distanceTraversed += stepDist;
            }
        }
        m_lastPosX = packet.player.posX;
        m_lastPosY = packet.player.posY;
    }

    // 1. Kiểm tra tử trận
    if (packet.player.maxHP > 0 && packet.player.currentHP == 0) {
        if (m_state != CharacterState::DeadOrRespawning) {
            TransitionTo(CharacterState::DeadOrRespawning, nowMs);
        }
        return;
    }
    if (m_state == CharacterState::DeadOrRespawning && packet.player.currentHP > 0) {
        // Hồi sinh hoàn tất -> phục hồi theo khu vực
        if (currentZone == ZoneKind::Hideout) TransitionTo(CharacterState::HideoutIdle, nowMs);
        else if (currentZone == ZoneKind::Town) TransitionTo(CharacterState::TownIdle, nowMs);
        else TransitionTo(CharacterState::InMapExploring, nowMs);
    }

    // 2. Kiểm tra chuyển Instance / nạp bản đồ mới (Area Seed thay đổi)
    if (packet.area.areaSeed != 0 && packet.area.areaSeed != m_lastAreaSeed) {
        m_lastAreaSeed = packet.area.areaSeed;
        if (currentZone == ZoneKind::Hideout) {
            TransitionTo(CharacterState::HideoutIdle, nowMs);
        } else if (currentZone == ZoneKind::Town) {
            TransitionTo(CharacterState::TownIdle, nowMs);
        } else if (currentZone == ZoneKind::CombatMap) {
            TransitionTo(CharacterState::InMapExploring, nowMs);
        }
    }

    // 3. Khởi tạo lần đầu
    if (m_state == CharacterState::Uninitialized) {
        if (currentZone == ZoneKind::Hideout) TransitionTo(CharacterState::HideoutIdle, nowMs);
        else if (currentZone == ZoneKind::Town) TransitionTo(CharacterState::TownIdle, nowMs);
        else if (currentZone == ZoneKind::CombatMap) TransitionTo(CharacterState::InMapExploring, nowMs);
    }

    // 4. Máy trạng thái phân nhánh theo từng State
    switch (m_state) {
        case CharacterState::HideoutIdle: {
            if (m_pendingStashAfterMap) {
                if (m_invHandler != nullptr && m_invHandler->IsActive()) {
                    m_pendingStashAfterMap = false;
                    TransitionTo(CharacterState::HideoutStashing, nowMs);
                    break;
                }
                if (nowMs - m_hideoutEnterMs < kStashGateMs) {
                    break;
                }
                m_pendingStashAfterMap = false;
            }
            // INV-FSM-HIDEOUT-F8-MAP-LOOP: Bật F8 tại Hideout -> tự động chuyển tiếp cận Map Device
            if (m_autonomousEnabled && m_mapDeviceHandler != nullptr) {
                if (m_lastMapDeviceFailMs == 0 || (nowMs - m_lastMapDeviceFailMs >= kMapDeviceRetryCooldownMs)) {
                    TriggerMapDeviceRoutine(nowMs);
                    break;
                }
            }
            if (m_invHandler != nullptr && m_invHandler->IsActive()) {
                TransitionTo(CharacterState::HideoutStashing, nowMs);
                break;
            }
            if (m_npcHandler != nullptr && m_npcHandler->IsActive()) {
                TransitionTo(CharacterState::HideoutVendoring, nowMs);
                break;
            }
            break;
        }

        case CharacterState::HideoutMapDeviceApproach: {
            if (m_mapDeviceHandler == nullptr || !m_mapDeviceHandler->IsActive()) {
                m_lastMapDeviceFailMs = nowMs;
                TransitionTo(CharacterState::HideoutIdle, nowMs);
                break;
            }
            const MapDeviceState mds = m_mapDeviceHandler->State();
            if (mds == MapDeviceState::OpenDevice ||
                mds == MapDeviceState::SelectAtlasNode ||
                mds == MapDeviceState::InspectPrecursorTowers ||
                mds == MapDeviceState::PlaceWaystone ||
                mds == MapDeviceState::PlaceTablet ||
                mds == MapDeviceState::Activate ||
                mds == MapDeviceState::WaitForPortals) {
                TransitionTo(CharacterState::HideoutMapDeviceOpen, nowMs);
            } else if (mds == MapDeviceState::EnterPortal) {
                TransitionTo(CharacterState::HideoutPortalEnter, nowMs);
            } else if (mds == MapDeviceState::Failed) {
                m_lastMapDeviceFailMs = nowMs;
                TransitionTo(CharacterState::HideoutIdle, nowMs);
            }
            break;
        }

        case CharacterState::HideoutMapDeviceOpen: {
            if (m_mapDeviceHandler == nullptr || !m_mapDeviceHandler->IsActive()) {
                m_lastMapDeviceFailMs = nowMs;
                TransitionTo(CharacterState::HideoutIdle, nowMs);
                break;
            }
            const MapDeviceState mds = m_mapDeviceHandler->State();
            if (mds == MapDeviceState::EnterPortal) {
                TransitionTo(CharacterState::HideoutPortalEnter, nowMs);
            } else if (mds == MapDeviceState::Failed) {
                m_lastMapDeviceFailMs = nowMs;
                TransitionTo(CharacterState::HideoutIdle, nowMs);
            }
            break;
        }

        case CharacterState::HideoutPortalEnter: {
            if (currentZone == ZoneKind::CombatMap) {
                TransitionTo(CharacterState::InMapExploring, nowMs);
            } else if (m_mapDeviceHandler != nullptr && !m_mapDeviceHandler->IsActive()) {
                m_lastMapDeviceFailMs = nowMs;
                TransitionTo(CharacterState::HideoutIdle, nowMs);
            }
            break;
        }

        case CharacterState::InMapExploring: {
            if (currentZone == ZoneKind::Hideout) {
                TransitionTo(CharacterState::HideoutIdle, nowMs);
                break;
            } else if (currentZone == ZoneKind::Town) {
                TransitionTo(CharacterState::TownIdle, nowMs);
                break;
            }

            // Đếm quái sống và kiểm tra vật phẩm rơi
            uint32_t liveMonstersNear = 0;
            uint32_t lootItemsNear = 0;

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

                if (ent.type == 1 && !(ent.extraFlags & 4) && ent.maxHP > 0) {
                    if (dist <= 380.0f) {
                        ++liveMonstersNear;
                    }
                } else if (ent.type == 2) {
                    if (dist <= 120.0f) {
                        ++lootItemsNear;
                    }
                }
            }

            if (liveMonstersNear > 0) {
                m_mapQuietStartMs = 0;
                TransitionTo(CharacterState::InMapCombat, nowMs);
            } else if (lootItemsNear > 0) {
                m_mapQuietStartMs = 0;
                TransitionTo(CharacterState::InMapLooting, nowMs);
            } else {
                if (m_mapQuietStartMs == 0) {
                    m_mapQuietStartMs = nowMs;
                }
                if (CheckMapCompleted(packet, nowMs)) {
                    TransitionTo(CharacterState::InMapPortalExit, nowMs);
                }
            }
            break;
        }

        case CharacterState::InMapCombat: {
            if (currentZone == ZoneKind::Hideout) {
                TransitionTo(CharacterState::HideoutIdle, nowMs);
                break;
            } else if (currentZone == ZoneKind::Town) {
                TransitionTo(CharacterState::TownIdle, nowMs);
                break;
            }

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

                if (ent.type == 1 && !(ent.extraFlags & 4) && ent.maxHP > 0) {
                    if (dist <= 450.0f) {
                        ++liveMonsters;
                    }
                } else if (ent.type == 2) {
                    if (dist <= 120.0f) {
                        ++lootItems;
                    }
                }
            }

            if (liveMonsters == 0) {
                if (lootItems > 0) {
                    TransitionTo(CharacterState::InMapLooting, nowMs);
                } else {
                    TransitionTo(CharacterState::InMapExploring, nowMs);
                }
            }
            break;
        }

        case CharacterState::InMapLooting: {
            if (currentZone == ZoneKind::Hideout) {
                TransitionTo(CharacterState::HideoutIdle, nowMs);
                break;
            } else if (currentZone == ZoneKind::Town) {
                TransitionTo(CharacterState::TownIdle, nowMs);
                break;
            }

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

                if (ent.type == 1 && !(ent.extraFlags & 4) && ent.maxHP > 0) {
                    if (dist <= 70.0f) {
                        ++threateningMonsters;
                    }
                } else if (ent.type == 2) {
                    if (dist <= 120.0f) {
                        ++lootItems;
                    }
                }
            }

            if (threateningMonsters > 0) {
                TransitionTo(CharacterState::InMapCombat, nowMs);
            } else if (lootItems == 0) {
                TransitionTo(CharacterState::InMapExploring, nowMs);
            }
            break;
        }

        case CharacterState::InMapPortalExit: {
            if (currentZone == ZoneKind::Hideout) {
                TransitionTo(CharacterState::HideoutIdle, nowMs);
            } else if (currentZone == ZoneKind::Town) {
                TransitionTo(CharacterState::TownIdle, nowMs);
            }
            break;
        }

        case CharacterState::HideoutStashing: {
            if (m_invHandler == nullptr || !m_invHandler->IsActive()) {
                TransitionTo(CharacterState::HideoutIdle, nowMs);
            }
            break;
        }

        case CharacterState::HideoutVendoring: {
            if (m_npcHandler == nullptr || !m_npcHandler->IsActive()) {
                TransitionTo(CharacterState::HideoutIdle, nowMs);
            }
            break;
        }

        default:
            break;
    }
}
