#include "combat/skill_engine.hpp"

#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#include "common/logger.hpp"

namespace {

static std::string Trim(const std::string& str) {
    size_t start = str.find_first_not_of(" \t\r\n");
    if (start == std::string::npos) return "";
    size_t end = str.find_last_not_of(" \t\r\n");
    return str.substr(start, end - start + 1);
}

static bool ParseTomlKeyVal(const std::string& line, const std::string& key, std::string& outVal) {
    size_t eq = line.find('=');
    if (eq == std::string::npos) return false;
    std::string keyPart = Trim(line.substr(0, eq));
    if (keyPart != key) return false;
    outVal = Trim(line.substr(eq + 1));
    return true;
}

static bool ParseTomlString(const std::string& line, const std::string& key, std::string& outVal) {
    std::string valPart;
    if (!ParseTomlKeyVal(line, key, valPart)) return false;
    if (valPart.size() >= 2 && valPart.front() == '"' && valPart.back() == '"') {
        outVal = valPart.substr(1, valPart.size() - 2);
        return true;
    }
    size_t q1 = valPart.find('"');
    size_t q2 = valPart.find('"', q1 + 1);
    if (q1 != std::string::npos && q2 != std::string::npos) {
        outVal = valPart.substr(q1 + 1, q2 - q1 - 1);
        return true;
    }
    outVal = valPart;
    return true;
}

static bool ParseTomlFloat(const std::string& line, const std::string& key, float& outVal) {
    std::string valPart;
    if (!ParseTomlKeyVal(line, key, valPart)) return false;
    try {
        outVal = std::stof(valPart);
        return true;
    } catch (...) {
        return false;
    }
}

static bool ParseTomlUint(const std::string& line, const std::string& key, uint32_t& outVal) {
    std::string valPart;
    if (!ParseTomlKeyVal(line, key, valPart)) return false;
    try {
        outVal = static_cast<uint32_t>(std::stoul(valPart));
        return true;
    } catch (...) {
        return false;
    }
}

static bool ParseTomlBool(const std::string& line, const std::string& key, bool& outVal) {
    std::string valPart;
    if (!ParseTomlKeyVal(line, key, valPart)) return false;
    if (valPart.find("true") != std::string::npos) { outVal = true; return true; }
    if (valPart.find("false") != std::string::npos) { outVal = false; return true; }
    return false;
}

} // namespace

// ==========================================================
// SkillProfile Implementation
// ==========================================================

void SkillProfile::Clear() {
    m_skills.clear();
    m_trackers.clear();
}

const FlexibleSkill* SkillProfile::FindSkillByVk(uint8_t vk) const {
    for (const auto& s : m_skills) {
        if (s.vkCode == vk) return &s;
    }
    return nullptr;
}

FlexibleSkill* SkillProfile::FindSkillByVk(uint8_t vk) {
    for (auto& s : m_skills) {
        if (s.vkCode == vk) return &s;
    }
    return nullptr;
}

const FlexibleSkill* SkillProfile::FindSkillByName(const std::string& name) const {
    for (const auto& s : m_skills) {
        if (s.name == name || s.assetName == name) return &s;
    }
    return nullptr;
}

FlexibleSkill* SkillProfile::FindSkillByName(const std::string& name) {
    for (auto& s : m_skills) {
        if (s.name == name || s.assetName == name) return &s;
    }
    return nullptr;
}

int64_t SkillProfile::GetTracker(const std::string& name) const {
    auto it = m_trackers.find(name);
    return (it != m_trackers.end()) ? it->second : 0;
}

void SkillProfile::SetTracker(const std::string& name, int64_t value) {
    m_trackers[name] = value;
}

void SkillProfile::IncrementTracker(const std::string& name, int64_t delta) {
    m_trackers[name] += delta;
}

void SkillProfile::ResetTracker(const std::string& name) {
    m_trackers[name] = 0;
}

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

    std::stringstream buffer;
    buffer << file.rdbuf();
    return ParseFromString(buffer.str());
}

bool SkillProfile::ParseFromString(const std::string& content) {
    Clear();

    std::stringstream ss(content);
    std::string rawLine;
    FlexibleSkill currentSkill{};
    bool insideSkill = false;

    // Temporary variables for parsing
    uint32_t targetRarityMin = 0;
    uint32_t minComboPoints = 0;
    uint32_t maxComboPoints = 0;
    uint32_t minRage = 0;
    uint32_t maxRage = 0;
    float minLifePct = 0.0f;
    float maxLifePct = 2.0f;
    float minEsPct = 0.0f;
    float maxEsPct = 2.0f;
    float minManaPct = 0.0f;
    float maxManaPct = 2.0f;
    bool requiresTargetMarked = false;
    bool requiresTargetUnmarked = false;
    bool requiresBell = false;

    auto FinalizeSkill = [&](FlexibleSkill& s) {
        s.vkCode = SkillsTable::KeyStringToVk(s.keyStr);

        // Tự động sinh preconditions từ các thuộc tính đã phân tích
        if (targetRarityMin > 0) {
            ConditionRule r{};
            r.type = ConditionType::TargetRarityMin;
            r.intValue = targetRarityMin;
            s.preconditions.push_back(r);
        }
        if (minComboPoints > 0) {
            ConditionRule r{};
            r.type = ConditionType::MinComboPoints;
            r.intValue = minComboPoints;
            s.preconditions.push_back(r);
        }
        if (maxComboPoints > 0) {
            ConditionRule r{};
            r.type = ConditionType::MaxComboPoints;
            r.intValue = maxComboPoints;
            s.preconditions.push_back(r);
        }
        if (minRage > 0) {
            ConditionRule r{};
            r.type = ConditionType::MinRage;
            r.intValue = minRage;
            s.preconditions.push_back(r);
        }
        if (maxRage > 0) {
            ConditionRule r{};
            r.type = ConditionType::MaxRage;
            r.intValue = maxRage;
            s.preconditions.push_back(r);
        }
        if (minLifePct > 0.001f) {
            ConditionRule r{};
            r.type = ConditionType::MinLifePct;
            r.floatValue = minLifePct;
            s.preconditions.push_back(r);
        }
        if (maxLifePct < 1.999f) {
            ConditionRule r{};
            r.type = ConditionType::MaxLifePct;
            r.floatValue = maxLifePct;
            s.preconditions.push_back(r);
        }
        if (minEsPct > 0.001f) {
            ConditionRule r{};
            r.type = ConditionType::MinEsPct;
            r.floatValue = minEsPct;
            s.preconditions.push_back(r);
        }
        if (maxEsPct < 1.999f) {
            ConditionRule r{};
            r.type = ConditionType::MaxEsPct;
            r.floatValue = maxEsPct;
            s.preconditions.push_back(r);
        }
        if (minManaPct > 0.001f) {
            ConditionRule r{};
            r.type = ConditionType::MinManaPct;
            r.floatValue = minManaPct;
            s.preconditions.push_back(r);
        }
        if (maxManaPct < 1.999f) {
            ConditionRule r{};
            r.type = ConditionType::MaxManaPct;
            r.floatValue = maxManaPct;
            s.preconditions.push_back(r);
        }
        if (requiresTargetMarked) {
            ConditionRule r{};
            r.type = ConditionType::TargetDebuffRequired;
            s.preconditions.push_back(r);
        }
        if (requiresTargetUnmarked) {
            ConditionRule r{};
            r.type = ConditionType::TargetDebuffAbsent;
            s.preconditions.push_back(r);
        }
        if (requiresBell) {
            ConditionRule r{};
            r.type = ConditionType::FieldEntityNear;
            r.floatValue = 250.0f;
            s.preconditions.push_back(r);
        }

        // Tự động nhận diện Effects đặc thù của build nếu chưa được gán
        if (s.effects.empty()) {
            if (s.assetName.find("BearWarcry") != std::string::npos || s.name.find("Warcry") != std::string::npos) {
                s.effects.push_back({ EffectType::GrantRage, "rage", 30, 0 });
                // Warcry cho rare/boss thường yêu cầu rage chưa đầy (< 20)
                if (maxRage == 0) {
                    ConditionRule r{};
                    r.type = ConditionType::MaxRage;
                    r.intValue = 20;
                    s.preconditions.push_back(r);
                }
            } else if (s.assetName.find("WolfLeapAttack") != std::string::npos || s.name.find("Wolf") != std::string::npos) {
                s.effects.push_back({ EffectType::ApplyTargetDebuff, "Marked_Vulnerability", 0, 6000 });
            } else if (s.assetName.find("TempestFlurry") != std::string::npos || s.name.find("Combo Builder") != std::string::npos) {
                s.effects.push_back({ EffectType::IncrementTracker, "combo_points", 1, 0 });
            } else if (s.assetName.find("TempestBell") != std::string::npos || s.name.find("Bell") != std::string::npos) {
                s.effects.push_back({ EffectType::ResetTracker, "combo_points", 0, 0 });
                s.effects.push_back({ EffectType::SpawnFieldEntity, "ResonanceBell", 0, 8000 });
                if (s.targetMarkedMultiplier <= 1.0f) {
                    s.targetMarkedMultiplier = 1.5f; // Tăng ưu tiên khi target bị mark
                }
                // Nếu chưa có min_combo_points trong toml, gán mặc định là 4
                if (minComboPoints == 0) {
                    ConditionRule r{};
                    r.type = ConditionType::TrackerExact;
                    r.resourceName = "combo_points";
                    r.intValue = 4;
                    s.preconditions.push_back(r);
                }
            }
        }

        m_skills.push_back(s);
    };

    while (std::getline(ss, rawLine)) {
        size_t cPos = rawLine.find('#');
        std::string line = (cPos != std::string::npos) ? rawLine.substr(0, cPos) : rawLine;
        line = Trim(line);
        if (line.empty()) continue;

        if (line == "[[skill]]") {
            if (insideSkill) {
                FinalizeSkill(currentSkill);
            }
            currentSkill = FlexibleSkill{};
            targetRarityMin = 0;
            minComboPoints = 0;
            maxComboPoints = 0;
            minRage = 0;
            maxRage = 0;
            minLifePct = 0.0f;
            maxLifePct = 2.0f;
            minEsPct = 0.0f;
            maxEsPct = 2.0f;
            minManaPct = 0.0f;
            maxManaPct = 2.0f;
            requiresTargetMarked = false;
            requiresTargetUnmarked = false;
            requiresBell = false;
            insideSkill = true;
            continue;
        }

        if (!insideSkill) {
            std::string profName;
            if (ParseTomlString(line, "profile_name", profName)) {
                m_name = profName;
            }
            continue;
        }

        std::string sVal;
        if (ParseTomlString(line, "name", sVal)) { currentSkill.name = sVal; continue; }
        if (ParseTomlString(line, "asset_name", sVal)) { currentSkill.assetName = sVal; continue; }
        if (ParseTomlString(line, "key", sVal)) { currentSkill.keyStr = sVal; continue; }
        if (ParseTomlString(line, "kind", sVal)) { currentSkill.kind = StringToSkillKind(sVal); continue; }

        uint32_t uVal = 0;
        if (ParseTomlUint(line, "priority", uVal)) { currentSkill.basePriority = uVal; continue; }
        if (ParseTomlUint(line, "cooldown_ms", uVal)) { currentSkill.cooldownMs = uVal; continue; }
        if (ParseTomlUint(line, "windup_ms", uVal)) { currentSkill.windupMs = uVal; continue; }
        if (ParseTomlUint(line, "active_ms", uVal)) { currentSkill.activeMs = uVal; continue; }
        if (ParseTomlUint(line, "recovery_ms", uVal)) { currentSkill.recoveryMs = uVal; continue; }
        if (ParseTomlUint(line, "target_rarity_min", uVal)) { targetRarityMin = uVal; continue; }
        if (ParseTomlUint(line, "min_combo_points", uVal)) { minComboPoints = uVal; continue; }
        if (ParseTomlUint(line, "max_combo_points", uVal)) { maxComboPoints = uVal; continue; }
        if (ParseTomlUint(line, "min_rage", uVal)) { minRage = uVal; continue; }
        if (ParseTomlUint(line, "max_rage", uVal)) { maxRage = uVal; continue; }

        float fVal = 0.0f;
        if (ParseTomlFloat(line, "min_distance", fVal)) { currentSkill.minDistance = fVal; continue; }
        if (ParseTomlFloat(line, "max_distance", fVal)) { currentSkill.maxDistance = fVal; continue; }
        if (ParseTomlFloat(line, "min_life_pct", fVal)) { minLifePct = fVal; continue; }
        if (ParseTomlFloat(line, "max_life_pct", fVal)) { maxLifePct = fVal; continue; }
        if (ParseTomlFloat(line, "min_mana_pct", fVal)) { minManaPct = fVal; continue; }
        if (ParseTomlFloat(line, "max_mana_pct", fVal)) { maxManaPct = fVal; continue; }
        if (ParseTomlFloat(line, "min_es_pct", fVal)) { minEsPct = fVal; continue; }
        if (ParseTomlFloat(line, "max_es_pct", fVal)) { maxEsPct = fVal; continue; }
        if (ParseTomlFloat(line, "target_marked_multiplier", fVal)) { currentSkill.targetMarkedMultiplier = fVal; continue; }

        bool bVal = false;
        if (ParseTomlBool(line, "maintain", bVal)) { currentSkill.maintain = bVal; continue; }
        if (ParseTomlBool(line, "requires_target_marked", bVal)) { requiresTargetMarked = bVal; continue; }
        if (ParseTomlBool(line, "requires_target_unmarked", bVal)) { requiresTargetUnmarked = bVal; continue; }
        if (ParseTomlBool(line, "requires_bell", bVal)) { requiresBell = bVal; continue; }
    }

    if (insideSkill) {
        FinalizeSkill(currentSkill);
    }

    std::string logMsg = "[SkillProfile] Nap thanh cong " + std::to_string(m_skills.size()) +
                         " skills vao profile '" + m_name + "'";
    std::cout << logMsg << std::endl;
    CoreLog(logMsg);
    return true;
}

// ==========================================================
// SkillEngine Implementation
// ==========================================================

SkillEngine::SkillEngine() {
    m_activeProfile = std::make_shared<SkillProfile>();
}

bool SkillEngine::LoadProfile(const std::string& tomlPath) {
    auto newProf = std::make_shared<SkillProfile>();
    if (!newProf->LoadFromToml(tomlPath)) {
        return false;
    }
    return HotSwapProfile(newProf);
}

bool SkillEngine::HotSwapProfile(std::shared_ptr<SkillProfile> newProfile) {
    if (!newProfile || newProfile->Skills().empty()) {
        CoreLog("[SkillEngine] Tu choi hot-swap profile rong hoac nullptr");
        return false;
    }
    std::lock_guard<std::mutex> lock(m_profileMutex);
    m_activeProfile = newProfile;
    CoreLog("[SkillEngine] Hoan doi profile thanh cong: " + newProfile->Name());
    return true;
}

std::shared_ptr<SkillProfile> SkillEngine::ActiveProfile() const {
    std::lock_guard<std::mutex> lock(m_profileMutex);
    return m_activeProfile;
}

bool SkillEngine::IsTargetMarked(uint64_t nowMs) const {
    return (m_markExpiryMs > 0 && nowMs < m_markExpiryMs);
}

void SkillEngine::SetTargetMark(uint32_t targetId, uint32_t durationMs, uint64_t nowMs) {
    m_markedTargetId = (targetId != 0) ? targetId : 1;
    m_markExpiryMs = nowMs + durationMs;
    CoreLog("[SkillEngine] Gán nhãn Marked_Vulnerability lên Target ID " + std::to_string(m_markedTargetId) +
            " (Thời hạn: " + std::to_string(durationMs) + "ms)");
}

void SkillEngine::ClearTargetMark() {
    m_markedTargetId = 0;
    m_markExpiryMs = 0;
}

bool SkillEngine::IsBellActive(uint64_t nowMs) const {
    return m_bellActive && (m_bellExpiryMs == 0 || nowMs < m_bellExpiryMs);
}

void SkillEngine::SpawnBell(float x, float y, uint32_t durationMs, uint64_t nowMs) {
    m_bellActive = true;
    m_bellX = x;
    m_bellY = y;
    m_bellExpiryMs = nowMs + durationMs;
    CoreLog("[SkillEngine] Cắm Tempest Bell tại (" + std::to_string(x) + ", " + std::to_string(y) +
            ") - Tồn tại " + std::to_string(durationMs) + "ms");
}

void SkillEngine::ClearBell() {
    m_bellActive = false;
    m_bellExpiryMs = 0;
}

void SkillEngine::ResetState() {
    m_animState = AnimationState{};
    ClearTargetMark();
    ClearBell();
    if (m_activeProfile) {
        m_activeProfile->Clear();
    }
}

SkillDecision SkillEngine::SelectBestAction(CombatContext ctx, uint64_t nowMs) {
    // 1. Cập nhật pha hoạt ảnh
    m_animState.UpdatePhase(nowMs);

    if (ctx.target.id != 0) {
        m_markedTargetId = ctx.target.id;
    }

    // 2. Đồng bộ trạng thái debuff và thực thể vào context
    if (IsTargetMarked(nowMs)) {
        ctx.target.hasMarkedDebuff = true;
        ctx.target.markExpiryMs = m_markExpiryMs;
    }

    if (IsBellActive(nowMs)) {
        ctx.env.hasFieldBell = true;
        ctx.env.bellPosX = m_bellX;
        ctx.env.bellPosY = m_bellY;
        ctx.env.bellExpiryMs = m_bellExpiryMs;
        float dx = ctx.target.posX - m_bellX;
        float dy = ctx.target.posY - m_bellY;
        ctx.env.bellDistance = std::sqrt(dx * dx + dy * dy);
    }

    // 3. Cơ chế Khẩn cấp: Emergency Animation Cancel khi ES tụt thấp (< 70%)
    if (m_emergencyCancelEnabled) {
        float esRatio = ctx.vitals.EsPct();
        bool isLowEs = (ctx.vitals.maxES > 0) ? (esRatio < m_emergencyEsPct) : (ctx.vitals.LifePct() < 0.45f);

        if (isLowEs && (ctx.target.distance < 300.0f || ctx.nearbyEnemyCount > 0)) {
            // Nếu đang bị kẹt trong hoạt ảnh và có thể cancel, ngắt chiêu ngay lập tức
            if (m_animState.IsLocked(nowMs) && m_animState.canEmergencyCancel) {
                OnSkillInterrupted(nowMs);
                SkillDecision dec{};
                dec.kind = SkillDecisionKind::EmergencyCancel;
                dec.vkCode = m_emergencyDodgeVk;
                dec.skillName = "Emergency Dodge Roll";
                dec.priority = 10000;
                dec.reason = "LOW ES (" + std::to_string((int)(esRatio * 100)) + "% < " +
                             std::to_string((int)(m_emergencyEsPct * 100)) + "%) - Emergency Cancel!";
                return dec;
            }
        }
    }

    // 4. Kiểm tra Animation Lock thông thường
    if (m_animState.IsLocked(nowMs)) {
        SkillDecision dec{};
        dec.kind = SkillDecisionKind::WaitAnimation;
        dec.vkCode = m_animState.currentSkillVk;
        dec.reason = std::string("Animation Locked (Phase: ") + AnimationPhaseToString(m_animState.phase) + ")";
        return dec;
    }

    // 5. Tuyển chọn kỹ năng từ profile đang kích hoạt
    std::shared_ptr<SkillProfile> profile;
    {
        std::lock_guard<std::mutex> lock(m_profileMutex);
        profile = m_activeProfile;
    }

    if (!profile || profile->Skills().empty()) {
        SkillDecision dec{};
        dec.kind = SkillDecisionKind::Idle;
        dec.reason = "Profile rong hoac chua nap";
        return dec;
    }

    const FlexibleSkill* bestSkill = nullptr;
    uint32_t bestPriority = 999999;

    for (const auto& skill : profile->Skills()) {
        if (!skill.ArePreconditionsMet(ctx, profile->Trackers(), nowMs)) {
            continue;
        }

        uint32_t effPriority = skill.CalculateEffectivePriority(ctx, nowMs);

        // Priority càng thấp thì mức độ ưu tiên càng cao (1 > 2 > 3...)
        if (bestSkill == nullptr || effPriority < bestPriority) {
            bestSkill = &skill;
            bestPriority = effPriority;
        }
    }

    if (bestSkill != nullptr) {
        SkillDecision dec{};
        dec.kind = SkillDecisionKind::CastSkill;
        dec.vkCode = bestSkill->vkCode;
        dec.skillName = bestSkill->name;
        dec.targetX = ctx.target.posX;
        dec.targetY = ctx.target.posY;
        dec.priority = bestPriority;
        dec.reason = "Selected " + bestSkill->name + " (Vk: 0x" +
                     std::to_string(bestSkill->vkCode) + ", Priority: " + std::to_string(bestPriority) + ")";
        return dec;
    }

    SkillDecision dec{};
    dec.kind = SkillDecisionKind::Idle;
    dec.reason = "Khong co ky nang nao thoa man preconditions";
    return dec;
}

void SkillEngine::OnSkillCastStarted(uint8_t vkCode, uint64_t nowMs) {
    m_animState.phase = AnimationPhase::Windup;
    m_animState.currentSkillVk = vkCode;
    m_animState.phaseStartMs = nowMs;

    // Tìm skill để nạp đúng thời gian windup/active/recovery
    std::shared_ptr<SkillProfile> profile = ActiveProfile();
    if (profile) {
        const FlexibleSkill* skill = profile->FindSkillByVk(vkCode);
        if (skill) {
            m_animState.windupDurationMs = skill->windupMs;
            m_animState.activeDurationMs = skill->activeMs;
            m_animState.recoveryDurationMs = skill->recoveryMs;
            return;
        }
    }

    // Default timings
    m_animState.windupDurationMs = 120;
    m_animState.activeDurationMs = 80;
    m_animState.recoveryDurationMs = 120;
}

void SkillEngine::OnSkillExecuted(uint8_t vkCode, uint64_t nowMs) {
    std::shared_ptr<SkillProfile> profile = ActiveProfile();
    if (profile) {
        FlexibleSkill* skill = profile->FindSkillByVk(vkCode);
        if (skill) {
            skill->lastCastMs = nowMs;
            ApplySkillEffects(*skill, nowMs);
        }
    }

    // Chuyển sang phase Recovery
    m_animState.phase = AnimationPhase::Recovery;
    m_animState.phaseStartMs = nowMs;
}

void SkillEngine::OnSkillInterrupted(uint64_t nowMs) {
    (void)nowMs;
    m_animState.phase = AnimationPhase::Idle;
    m_animState.currentSkillVk = 0;
    m_animState.phaseStartMs = 0;
    CoreLog("[SkillEngine] Hoạt ảnh kỹ năng đã bị ngắt khẩn cấp (Animation Interrupted)!");
}

void SkillEngine::ApplySkillEffects(const FlexibleSkill& skill, uint64_t nowMs) {
    std::shared_ptr<SkillProfile> profile = ActiveProfile();
    if (!profile) return;

    for (const auto& eff : skill.effects) {
        switch (eff.type) {
            case EffectType::IncrementTracker: {
                profile->IncrementTracker(eff.targetName, eff.value);
                // Giới hạn max 4 combo points nếu là combo_points
                if (eff.targetName == "combo_points") {
                    if (profile->GetTracker("combo_points") > 4) {
                        profile->SetTracker("combo_points", 4);
                    }
                }
                break;
            }
            case EffectType::ResetTracker:
                profile->ResetTracker(eff.targetName);
                break;
            case EffectType::SetTracker:
                profile->SetTracker(eff.targetName, eff.value);
                break;
            case EffectType::ApplyTargetDebuff:
                SetTargetMark(m_markedTargetId, eff.durationMs, nowMs);
                break;
            case EffectType::SpawnFieldEntity:
                SpawnBell(m_bellX, m_bellY, eff.durationMs, nowMs);
                break;
            case EffectType::GrantBuff:
                // Grant buff
                break;
            case EffectType::GrantRage:
                profile->IncrementTracker("rage", eff.value);
                break;
        }
    }
}
