#include "combat/skills_table.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 ParseTomlString(const std::string& line, const std::string& key, std::string& outVal) {
    size_t pos = line.find(key);
    if (pos == std::string::npos) return false;
    size_t eq = line.find('=', pos);
    if (eq == std::string::npos) return false;
    std::string valPart = Trim(line.substr(eq + 1));
    if (valPart.size() >= 2 && valPart.front() == '"' && valPart.back() == '"') {
        outVal = valPart.substr(1, valPart.size() - 2);
        return true;
    }
    // Also handle unquoted or comment stripped
    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) {
    size_t pos = line.find(key);
    if (pos == std::string::npos) return false;
    size_t eq = line.find('=', pos);
    if (eq == std::string::npos) return false;
    try {
        outVal = std::stof(line.substr(eq + 1));
        return true;
    } catch (...) {
        return false;
    }
}

static bool ParseTomlUint(const std::string& line, const std::string& key, uint32_t& outVal) {
    size_t pos = line.find(key);
    if (pos == std::string::npos) return false;
    size_t eq = line.find('=', pos);
    if (eq == std::string::npos) return false;
    try {
        outVal = static_cast<uint32_t>(std::stoul(line.substr(eq + 1)));
        return true;
    } catch (...) {
        return false;
    }
}

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

} // namespace

uint8_t SkillsTable::KeyStringToVk(const std::string& keyStr) {
    std::string k = keyStr;
    std::transform(k.begin(), k.end(), k.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });

    if (k == "mouse_right" || k == "rmb") return VK_RBUTTON;
    if (k == "mouse_left" || k == "lmb") return VK_LBUTTON;
    if (k == "mouse_middle" || k == "mmb") return VK_MBUTTON;
    if (k == "q") return 0x51;
    if (k == "w") return 0x57;
    if (k == "e") return 0x45;
    if (k == "r") return 0x52;
    if (k == "t") return 0x54;
    if (k == "f") return 0x46;
    if (k == "space" || k == "spacebar") return VK_SPACE;
    if (k == "1") return 0x31;
    if (k == "2") return 0x32;
    if (k == "3") return 0x33;
    if (k == "4") return 0x34;
    if (k == "5") return 0x35;
    return 0;
}

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

    m_skills.clear();
    SkillDefinition currentSkill{};
    bool insideSkill = false;

    std::string rawLine;
    while (std::getline(file, rawLine)) {
        // Strip comment
        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) {
                currentSkill.vkCode = KeyStringToVk(currentSkill.key);
                m_skills.push_back(currentSkill);
            }
            currentSkill = SkillDefinition{};
            insideSkill = true;
            continue;
        }

        if (!insideSkill) continue;

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

        uint32_t uVal = 0;
        if (ParseTomlUint(line, "priority", uVal)) { currentSkill.priority = uVal; continue; }
        if (ParseTomlUint(line, "cooldown_ms", uVal)) { currentSkill.cooldownMs = uVal; continue; }
        if (ParseTomlUint(line, "target_rarity_min", uVal)) { currentSkill.targetRarityMin = static_cast<uint8_t>(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, "close_targets_range", fVal)) { currentSkill.closeTargetsRange = fVal; continue; }
        if (ParseTomlUint(line, "close_targets", uVal)) { currentSkill.closeTargets = uVal; continue; }
        if (ParseTomlFloat(line, "min_life_pct", fVal)) { currentSkill.minLifePct = fVal; continue; }
        if (ParseTomlFloat(line, "max_life_pct", fVal)) { currentSkill.maxLifePct = fVal; continue; }
        if (ParseTomlFloat(line, "min_mana_pct", fVal)) { currentSkill.minManaPct = fVal; continue; }
        if (ParseTomlFloat(line, "max_mana_pct", fVal)) { currentSkill.maxManaPct = fVal; continue; }
        if (ParseTomlFloat(line, "min_es_pct", fVal)) { currentSkill.minEsPct = fVal; continue; }
        if (ParseTomlFloat(line, "max_es_pct", fVal)) { currentSkill.maxEsPct = fVal; continue; }

        bool bVal = false;
        if (ParseTomlBool(line, "maintain", bVal)) { currentSkill.maintain = bVal; continue; }
    }

    if (insideSkill) {
        currentSkill.vkCode = KeyStringToVk(currentSkill.key);
        m_skills.push_back(currentSkill);
    }

    std::string msg = "[SkillsTable] Da nap thanh cong " + std::to_string(m_skills.size()) +
                      " skills tu '" + tomlPath + "'";
    std::cout << msg << std::endl;
    CoreLog(msg);
    return true;
}

SkillDefinition* SkillsTable::SelectBestSkill(uint64_t nowMs, float dist, uint8_t rarity, float lifePct, float manaPct, float esPct, SkillKind requiredKind, uint32_t nearbyEnemies) {
    SkillDefinition* best = nullptr;

    for (auto& s : m_skills) {
        if (s.kind != requiredKind) continue;
        if (!s.IsEligible(nowMs, dist, rarity, lifePct, manaPct, esPct, nearbyEnemies)) continue;

        if (best == nullptr || s.priority < best->priority) {
            best = &s;
        }
    }

    return best;
}

void SkillsTable::MarkCast(const std::string& name, uint64_t nowMs) {
    for (auto& s : m_skills) {
        if (s.name == name) {
            s.lastCastMs = nowMs;
            return;
        }
    }
}

void SkillsTable::MarkCastByVk(uint8_t vk, uint64_t nowMs) {
    for (auto& s : m_skills) {
        if (s.vkCode == vk) {
            s.lastCastMs = nowMs;
            return;
        }
    }
}
