#include "combat/stutter_step_controller.hpp"
#include <chrono>

namespace combat {

StutterStepController::StutterStepController(const StutterStepConfig& config)
    : m_config(config) {
}

TargetEvaluation StutterStepController::EvaluateBestTarget(const TelemetryPacket& packet,
                                                          const navigation::TerrainGrid* grid,
                                                          uint32_t preferredTargetId,
                                                          CombatProfile profile,
                                                          int screenW, int screenH) const {
    TargetEvaluation best{};
    const auto& player = packet.player;
    const float maxRadiusSq = m_config.maxScanRadius * m_config.maxScanRadius;

    for (uint32_t i = 0; i < packet.entityCount; ++i) {
        const auto& ent = packet.entities[i];
        // Monster (type == 1), còn sống (!(extraFlags & 4)), HP > 0
        if (ent.type != 1 || (ent.extraFlags & 4) || ent.currentHP == 0) {
            continue;
        }

        const float dx = ent.posX - player.posX;
        const float dy = ent.posY - player.posY;
        const float distSq = dx * dx + dy * dy;
        if (distSq > maxRadiusSq) {
            continue;
        }

        const float dist = (distSq > 0.0001f) ? std::sqrt(distSq) : ent.distanceToPlayer;
        const bool los = grid ? grid->HasLineOfSight(player.posX, player.posY, ent.posX, ent.posY) : true;

        // Điểm số ưu tiên:
        // +1000 nếu có tầm nhìn thẳng (Line-of-Sight)
        // -dist: ưu tiên quái ở gần hơn
        // +3000 nếu là Boss khi ở profile BOSS_BURST hoặc ưu tiên Boss
        // +2000 nếu quái bị Staggered/Vulnerable
        // +1500 nếu khớp mục tiêu chỉ định
        float score = (los ? 1000.0f : 0.0f) - dist;
        const bool isBoss = (ent.extraFlags & 1) || (ent.rarity >= 3);
        if (isBoss) {
            score += (profile == CombatProfile::BOSS_BURST || m_config.prioritizeBoss) ? 3000.0f : 300.0f;
        }
        const bool isStaggered = (ent.staggerProgress >= 8000) || (ent.extraFlags & (1 << 7));
        if (isStaggered) {
            score += 2000.0f;
        }
        if (preferredTargetId != 0 && ent.id == preferredTargetId) {
            score += 1500.0f;
        }

        if (score > best.priorityScore) {
            best.entityId = ent.id;
            best.posX = ent.posX;
            best.posY = ent.posY;
            best.dx = (dx != 0.0f || dy != 0.0f) ? dx : 1.0f;
            best.dy = (dx != 0.0f || dy != 0.0f) ? dy : 0.0f;
            best.distance = dist;
            best.priorityScore = score;
            best.valid = true;
            best.isBoss = isBoss;
            best.isStaggered = isStaggered;
            best.hasLineOfSight = los;
        }
    }

    if (best.valid) {
        best.facingAngleRad = std::atan2(best.dy, best.dx);

        const float aimDist = (std::max)(120.0f, (std::min)(best.distance * 12.0f + 80.0f, static_cast<float>(screenH) * 0.42f));
        const float invLen = (best.distance > 0.001f) ? (1.0f / best.distance) : 0.0f;
        const float ndx = best.dx * invLen;
        const float ndy = best.dy * invLen;

        const float cx = static_cast<float>(screenW) * 0.5f;
        const float cy = static_cast<float>(screenH) * 0.5f;
        best.screenAimX = std::clamp(cx + (ndx - ndy) * aimDist * 0.70710678f, 100.0f, static_cast<float>(screenW) - 100.0f);
        best.screenAimY = std::clamp(cy - (ndx + ndy) * aimDist * 0.45f, 100.0f, static_cast<float>(screenH) - 100.0f);
    }

    m_stats.totalTargetsEvaluated++;
    return best;
}

bool StutterStepController::AutoFaceTarget(const TargetEvaluation& target,
                                          KMBoxNet& kmbox) {
    if (!target.valid || !m_config.enableAutoFacing) {
        return false;
    }

    kmbox.MoveMouse(static_cast<short>(target.screenAimX), static_cast<short>(target.screenAimY));
    m_lastFacingAngleRad = target.facingAngleRad;
    return true;
}

bool StutterStepController::StepTick(const TelemetryPacket& packet,
                                     const TargetEvaluation& target,
                                     navigation::MovementController& moveCtrl,
                                     input::NonBlockingActuator* actuator,
                                     KMBoxNet& kmbox,
                                     uint8_t skillVk,
                                     float desiredMoveDx, float desiredMoveDy,
                                     uint64_t nowMs) {
    (void)packet;

    if (!target.valid) {
        if (m_phase == StutterPhase::Casting) {
            if (nowMs >= m_castReleaseTimestampMs) {
                if (!actuator && m_currentCastingVk != 0) {
                    kmbox.KeyUpVk(m_currentCastingVk);
                }
                if (desiredMoveDx != 0.0f || desiredMoveDy != 0.0f) {
                    moveCtrl.SteerTowards(desiredMoveDx, desiredMoveDy, kmbox, nowMs);
                }
                m_phase = StutterPhase::Idle;
            }
        } else if (m_phase == StutterPhase::StepRecover) {
            if (nowMs - m_lastStepTimestampMs >= m_config.postCastStepDelayMs) {
                m_phase = StutterPhase::Idle;
            }
            if (desiredMoveDx != 0.0f || desiredMoveDy != 0.0f) {
                moveCtrl.SteerTowards(desiredMoveDx, desiredMoveDy, kmbox, nowMs);
            }
        } else {
            if (desiredMoveDx != 0.0f || desiredMoveDy != 0.0f) {
                moveCtrl.SteerTowards(desiredMoveDx, desiredMoveDy, kmbox, nowMs);
            }
        }
        return true;
    }

    m_lastTarget = target;

    switch (m_phase) {
    case StutterPhase::Idle: {
        // Tạm dừng di chuyển để đứng vững chuẩn bị ra đòn
        moveCtrl.Stop(kmbox);

        // Auto-facing quay mặt về mục tiêu
        AutoFaceTarget(target, kmbox);

        // Tung chiêu phi khóa
        if (actuator) {
            if (skillVk == VK_RBUTTON) {
                actuator->ClickMouse(2, m_config.minDwellCastMs, nowMs);
            } else if (skillVk == VK_LBUTTON) {
                actuator->ClickMouse(1, m_config.minDwellCastMs, nowMs);
            } else {
                actuator->KeyPressVk(skillVk, m_config.minDwellCastMs, nowMs);
            }
        } else {
            if (skillVk == VK_RBUTTON) {
                kmbox.ClickMouse(2, m_config.minDwellCastMs);
            } else if (skillVk == VK_LBUTTON) {
                kmbox.ClickMouse(1, m_config.minDwellCastMs);
            } else {
                kmbox.KeyDownVk(skillVk);
            }
        }

        m_currentCastingVk = skillVk;
        m_lastCastTimestampMs = nowMs;
        m_castReleaseTimestampMs = nowMs + m_config.minDwellCastMs;
        m_phase = StutterPhase::Casting;
        m_stats.totalCastsExecuted++;
        break;
    }

    case StutterPhase::Casting: {
        if (nowMs >= m_castReleaseTimestampMs) {
            if (!actuator && m_currentCastingVk != 0) {
                kmbox.KeyUpVk(m_currentCastingVk);
            }

            // MICRO-STUTTER-STEP: Ngay lập tức tái kích hoạt di chuyển!
            float stepDx = desiredMoveDx;
            float stepDy = desiredMoveDy;

            if (m_config.enableKitingStep && target.distance < m_config.safeKiteRadius) {
                // Quái áp sát quá gần: bước lùi kiting ngược hướng quái
                stepDx = -target.dx;
                stepDy = -target.dy;
            } else if (stepDx == 0.0f && stepDy == 0.0f) {
                // Bước nhẹ giữ cự ly tiếp cận
                stepDx = target.dx;
                stepDy = target.dy;
            }

            moveCtrl.SteerTowards(stepDx, stepDy, kmbox, nowMs);
            m_lastStepTimestampMs = nowMs;
            m_phase = StutterPhase::StepRecover;
            m_stats.totalStutterSteps++;
        }
        break;
    }

    case StutterPhase::StepRecover: {
        if (nowMs - m_lastStepTimestampMs >= m_config.postCastStepDelayMs) {
            // Hoàn tất chu kỳ bước stutter, sẵn sàng cho nhịp cast kế tiếp
            m_phase = StutterPhase::Idle;
        } else {
            // Duy trì di chuyển
            float stepDx = desiredMoveDx;
            float stepDy = desiredMoveDy;
            if (m_config.enableKitingStep && target.distance < m_config.safeKiteRadius) {
                stepDx = -target.dx;
                stepDy = -target.dy;
            }
            if (stepDx != 0.0f || stepDy != 0.0f) {
                moveCtrl.SteerTowards(stepDx, stepDy, kmbox, nowMs);
            }
        }
        break;
    }
    }

    return true;
}

void StutterStepController::Reset() {
    m_phase = StutterPhase::Idle;
    m_lastTarget = TargetEvaluation{};
    m_currentCastingVk = 0;
    m_lastCastTimestampMs = 0;
    m_castReleaseTimestampMs = 0;
    m_lastStepTimestampMs = 0;
    m_lastFacingAngleRad = 0.0f;
}

} // namespace combat
