#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdint>

#include "protocol.hpp"
#include "../combat/reflex_manager.hpp"
#include "../combat/combo_manager.hpp"
#include "../looting/loot_controller.hpp"
#include "../navigation/quest_navigator.hpp"
#include "../autologin/auto_login.hpp"
#include "../sensor/log_sensor.hpp"

namespace poe2::cli {

struct Poe2GameSettings {
    MovementMode moveMode = MovementMode::WASD;
    int resolutionWidth = 2560;
    int resolutionHeight = 1440;
    bool hideFilteredGroundItems = true;
    std::string accountName;
    uint8_t portalVk = 0xBE;
    uint8_t portalHid = 0x37;
};

inline std::string GetPoe2ConfigPath() {
    char userProfile[MAX_PATH] = {};
    GetEnvironmentVariableA("USERPROFILE", userProfile, sizeof(userProfile));

    std::vector<std::string> candidates = {
        std::string(userProfile) + "\\OneDrive\\Documents\\My Games\\Path of Exile 2\\poe2_production_Config.ini",
        std::string(userProfile) + "\\Documents\\My Games\\Path of Exile 2\\poe2_production_Config.ini",
        std::string(userProfile) + "\\OneDrive\\Documents\\My Games\\Path of Exile 2\\production_Config.ini",
        std::string(userProfile) + "\\Documents\\My Games\\Path of Exile 2\\production_Config.ini"
    };

    for (const auto& path : candidates) {
        if (std::filesystem::exists(path)) return path;
    }
    return "";
}

inline Poe2GameSettings ReadPoe2GameSettings() {
    Poe2GameSettings settings;
    std::string path = GetPoe2ConfigPath();
    if (path.empty()) return settings;

    std::ifstream file(path);
    if (!file.is_open()) return settings;

    std::string line;
    bool inActionKeys = false;
    bool inWasdActionKeys = false;

    bool modernWasdActive = false;
    while (std::getline(file, line)) {
        while (!line.empty() && (line.back() == '\r' || line.back() == ' ')) {
            line.pop_back();
        }

        if (line.find("[ACTION_KEYS]") != std::string::npos) {
            inActionKeys = true;
            inWasdActionKeys = false;
        } else if (line.find("[WASD_ACTION_KEYS]") != std::string::npos) {
            inWasdActionKeys = true;
            inActionKeys = false;
        } else if (!line.empty() && line.front() == '[') {
            inActionKeys = false;
            inWasdActionKeys = false;
        }

        if (inActionKeys) {
            if (line.find("move_up=") != std::string::npos) {
                if (line.find("move_up=0") == std::string::npos) {
                    settings.moveMode = MovementMode::WASD;
                }
            }
        }

        if (line.find("user_input_mode=wasd") != std::string::npos ||
            line.find("last_selected_KBM_input_mode=wasd") != std::string::npos) {
            settings.moveMode = MovementMode::WASD;
            modernWasdActive = true;
        } else if (line.find("use_wasd_to_move=false") != std::string::npos) {
            if (!modernWasdActive) {
                settings.moveMode = MovementMode::MOUSE;
            }
        } else if (line.find("use_wasd_to_move=true") != std::string::npos) {
            if (!modernWasdActive) {
                settings.moveMode = MovementMode::WASD;
            }
        }

        if (line.rfind("resolution_width=", 0) == 0) {
            try { settings.resolutionWidth = std::stoi(line.substr(17)); } catch (...) {}
        } else if (line.rfind("resolution_height=", 0) == 0) {
            try { settings.resolutionHeight = std::stoi(line.substr(18)); } catch (...) {}
        } else if (line.rfind("hide_all_filtered_ground_items=", 0) == 0) {
            settings.hideFilteredGroundItems = (line.substr(31) == "true");
        } else if (line.rfind("account_name=", 0) == 0) {
            auto val = line.substr(13);
            auto hashPos = val.find('#');
            if (hashPos != std::string::npos) val = val.substr(0, hashPos);
            settings.accountName = val;
        } else if (line.find("use_portal_scroll=") != std::string::npos) {
            try {
                auto valStr = line.substr(line.find("use_portal_scroll=") + 18);
                int keyVal = std::stoi(valStr);
                if (keyVal > 0) {
                    settings.portalVk = static_cast<uint8_t>(keyVal);
                    if (settings.portalVk == 0xBE) settings.portalHid = 0x37;
                    else if (settings.portalVk == 0x54) settings.portalHid = 0x17;
                    else if (settings.portalVk >= 'A' && settings.portalVk <= 'Z') settings.portalHid = static_cast<uint8_t>(0x04 + (settings.portalVk - 'A'));
                    else if (settings.portalVk >= '1' && settings.portalVk <= '9') settings.portalHid = static_cast<uint8_t>(0x1E + (settings.portalVk - '1'));
                    else if (settings.portalVk == '0') settings.portalHid = 0x27;
                    else settings.portalHid = 0x37;
                }
            } catch (...) {}
        }
    }
    if (modernWasdActive) {
        settings.moveMode = MovementMode::WASD;
    }
    return settings;
}

inline MovementMode DetectMovementModeFromGameConfig() {
    return ReadPoe2GameSettings().moveMode;
}

struct CoreCliOptions {
    bool allowPhysicalMove = false;
    bool calibrate = false;
    bool calibrateXyzOnly = false;
    std::string logPath = LogSensor::kDefaultLogPath;
    std::string backend = "auto";
    uintptr_t playerAddr = 0;
    uintptr_t manaAddrCmd = 0;
    uintptr_t entityOffsetCmd = 0;
    uint32_t expectedMaxMana = 189;
    uint32_t expectedMaxHP = 0;
    uint32_t expectedMaxES = 0;
    uint32_t expectedMaxWard = 0;
    uintptr_t wardAddrCmd = 0;

    ReflexConfig reflexCfg;
    LootConfig lootCfg;
    ComboConfig comboCfg;
    QuestNavConfig questCfg;
    AutoLoginConfig autoLoginCfg;
    MovementMode moveMode = MovementMode::WASD;
    bool explicitMoveModeFromCli = false;
};

inline CoreCliOptions ParseCommandLineArgs(int argc, char* argv[]) {
    CoreCliOptions opt;

    opt.reflexCfg.flaskEnabled = true;
    opt.reflexCfg.manaEnabled = true;
    opt.reflexCfg.dodgeEnabled = true;

    opt.lootCfg.enabled = true;

    opt.comboCfg.enabled = true;
    opt.comboCfg.primarySkillVk = VK_RBUTTON;
    opt.comboCfg.isRangedMode = true;

    opt.questCfg.enabled = true;
    opt.questCfg.combatStopDistance = 380.0f;
    opt.questCfg.isRanged = true;

    Poe2GameSettings gameSettings = ReadPoe2GameSettings();
    opt.moveMode = gameSettings.moveMode;
    opt.lootCfg.screenWidth = gameSettings.resolutionWidth;
    opt.lootCfg.screenHeight = gameSettings.resolutionHeight;
    opt.lootCfg.hideFilteredGroundItems = gameSettings.hideFilteredGroundItems;
    opt.questCfg.portalVk = gameSettings.portalVk;
    opt.questCfg.portalHid = gameSettings.portalHid;
    opt.reflexCfg.portalVk = gameSettings.portalVk;
    opt.reflexCfg.portalHid = gameSettings.portalHid;

    opt.autoLoginCfg.enabled = true;

    for (int i = 1; i < argc; ++i) {
        if (std::strcmp(argv[i], "--move") == 0) opt.allowPhysicalMove = true;
        if (std::strcmp(argv[i], "--wasd") == 0) { opt.moveMode = MovementMode::WASD; opt.explicitMoveModeFromCli = true; }
        if (std::strcmp(argv[i], "--mouse") == 0) { opt.moveMode = MovementMode::MOUSE; opt.explicitMoveModeFromCli = true; }
        if (std::strcmp(argv[i], "--move-mode") == 0 && i + 1 < argc) {
            std::string mm = argv[++i];
            for (char& c : mm) c = static_cast<char>(::tolower(static_cast<unsigned char>(c)));
            if (mm == "wasd") opt.moveMode = MovementMode::WASD;
            else opt.moveMode = MovementMode::MOUSE;
            opt.explicitMoveModeFromCli = true;
        }
        if (std::strcmp(argv[i], "--log-path") == 0 && i + 1 < argc) opt.logPath = argv[++i];
        if (std::strcmp(argv[i], "--backend") == 0 && i + 1 < argc) opt.backend = argv[++i];
        if (std::strcmp(argv[i], "--calibrate") == 0) opt.calibrate = true;
        if (std::strcmp(argv[i], "--calibrate-xyz") == 0) opt.calibrateXyzOnly = true;
        if (std::strcmp(argv[i], "--player-addr") == 0 && i + 1 < argc)
            opt.playerAddr = std::strtoull(argv[++i], nullptr, 16);
        if (std::strcmp(argv[i], "--autoflask") == 0 || std::strcmp(argv[i], "--smart-flask") == 0) { opt.reflexCfg.flaskEnabled = true; opt.reflexCfg.manaEnabled = true; }
        if (std::strcmp(argv[i], "--no-flask") == 0) { opt.reflexCfg.flaskEnabled = false; opt.reflexCfg.manaEnabled = false; }
        if (std::strcmp(argv[i], "--no-mana-flask") == 0) opt.reflexCfg.manaEnabled = false;
        if (std::strcmp(argv[i], "--no-life-flask") == 0) opt.reflexCfg.flaskEnabled = false;
        if (std::strcmp(argv[i], "--boss-rage-flask") == 0) opt.comboCfg.bossRageFlaskEnabled = true;
        if (std::strcmp(argv[i], "--no-boss-rage-flask") == 0) opt.comboCfg.bossRageFlaskEnabled = false;
        if (std::strcmp(argv[i], "--rage-flask-delay") == 0 && i + 1 < argc)
            opt.comboCfg.rageFlaskDelayAfterTMs = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--rage-buff-vk") == 0 && i + 1 < argc)
            opt.comboCfg.rageBuffSkillVk = static_cast<uint8_t>(std::strtoul(argv[++i], nullptr, 0));
        if (std::strcmp(argv[i], "--autododge") == 0 || std::strcmp(argv[i], "--auto-dodge") == 0) opt.reflexCfg.dodgeEnabled = true;
        if (std::strcmp(argv[i], "--no-dodge") == 0) opt.reflexCfg.dodgeEnabled = false;
        if (std::strcmp(argv[i], "--kiting") == 0) opt.reflexCfg.kitingEnabled = true;
        if (std::strcmp(argv[i], "--no-chicken") == 0) opt.reflexCfg.emergencyLogoutEnabled = false;
        if (std::strcmp(argv[i], "--flask-hp") == 0 && i + 1 < argc) {
            float val = static_cast<float>(std::atof(argv[++i]));
            opt.reflexCfg.hpThreshold = (val > 1.0f) ? (val / 100.0f) : val;
        }
        if ((std::strcmp(argv[i], "--mana-hp") == 0 || std::strcmp(argv[i], "--flask-mana") == 0) && i + 1 < argc) {
            float val = static_cast<float>(std::atof(argv[++i]));
            opt.reflexCfg.manaThreshold = (val > 1.0f) ? (val / 100.0f) : val;
        }
        if (std::strcmp(argv[i], "--burst-hp") == 0 && i + 1 < argc) {
            float val = static_cast<float>(std::atof(argv[++i]));
            opt.reflexCfg.burstDamageRatio = (val > 1.0f) ? (val / 100.0f) : val;
        }
        if (std::strcmp(argv[i], "--autoloot") == 0 || std::strcmp(argv[i], "--auto-loot") == 0) opt.lootCfg.enabled = true;
        if (std::strcmp(argv[i], "--no-loot") == 0) opt.lootCfg.enabled = false;
        if (std::strcmp(argv[i], "--fast-loot") == 0 || std::strcmp(argv[i], "--ultra-fast-loot") == 0) {
            opt.lootCfg.ultraFast = true;
            opt.lootCfg.lootIntervalMs = 25;
        }
        if (std::strcmp(argv[i], "--no-fast-loot") == 0) {
            opt.lootCfg.ultraFast = false;
            opt.lootCfg.lootIntervalMs = 150;
        }
        if (std::strcmp(argv[i], "--loot-interval") == 0 && i + 1 < argc)
            opt.lootCfg.lootIntervalMs = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--loot-radius") == 0 && i + 1 < argc)
            opt.lootCfg.lootRadius = static_cast<float>(std::atof(argv[++i]));
        if (std::strcmp(argv[i], "--loot-rarity") == 0 && i + 1 < argc)
            opt.lootCfg.minEquipmentRarity = static_cast<uint16_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--no-gold") == 0) opt.lootCfg.pickGold = false;
        if (std::strcmp(argv[i], "--no-currency") == 0) opt.lootCfg.pickCurrency = false;
        if (std::strcmp(argv[i], "--autocombo") == 0) opt.comboCfg.enabled = true;
        if (std::strcmp(argv[i], "--no-combo") == 0) opt.comboCfg.enabled = false;
        if (std::strcmp(argv[i], "--skill-rmb") == 0 || std::strcmp(argv[i], "--ranged") == 0) {
            opt.comboCfg.primarySkillVk = VK_RBUTTON;
            opt.comboCfg.isRangedMode = true;
            opt.questCfg.combatStopDistance = 380.0f;
            opt.questCfg.isRanged = true;
        }
        if (std::strcmp(argv[i], "--skill-lmb") == 0 || std::strcmp(argv[i], "--melee") == 0) {
            opt.comboCfg.primarySkillVk = VK_LBUTTON;
            opt.comboCfg.isRangedMode = false;
            opt.questCfg.combatStopDistance = 220.0f;
            opt.questCfg.isRanged = false;
        }
        if (std::strcmp(argv[i], "--primary-skill") == 0 && i + 1 < argc) {
            std::string ps = argv[++i];
            for (char& c : ps) c = static_cast<char>(::tolower(static_cast<unsigned char>(c)));
            if (ps == "rmb" || ps == "right" || ps == "rbutton") {
                opt.comboCfg.primarySkillVk = VK_RBUTTON;
                opt.comboCfg.isRangedMode = true;
                opt.questCfg.combatStopDistance = 380.0f;
                opt.questCfg.isRanged = true;
            } else if (ps == "lmb" || ps == "left" || ps == "lbutton") {
                opt.comboCfg.primarySkillVk = VK_LBUTTON;
                opt.comboCfg.isRangedMode = false;
                opt.questCfg.combatStopDistance = 220.0f;
                opt.questCfg.isRanged = false;
            } else if (ps == "f" || ps == "vk_f" || ps == "skill_f") {
                opt.comboCfg.primarySkillVk = 'F';
                opt.comboCfg.staffMeleeSkillVk = 'F';
                opt.comboCfg.isRangedMode = false;
                opt.questCfg.combatStopDistance = 220.0f;
                opt.questCfg.isRanged = false;
            }
        }
        if (std::strcmp(argv[i], "--skill-f") == 0) {
            opt.comboCfg.primarySkillVk = 'F';
            opt.comboCfg.staffMeleeSkillVk = 'F';
            opt.comboCfg.isRangedMode = false;
            opt.questCfg.combatStopDistance = 220.0f;
            opt.questCfg.isRanged = false;
        }
        if (std::strcmp(argv[i], "--autoquest") == 0) {
            opt.questCfg.enabled = true;
            opt.questCfg.autoPatrol = true;
        }
        if (std::strcmp(argv[i], "--no-quest") == 0) opt.questCfg.enabled = false;
        if (std::strcmp(argv[i], "--autopatrol") == 0) opt.questCfg.autoPatrol = true;
        if (std::strcmp(argv[i], "--no-patrol") == 0) opt.questCfg.autoPatrol = false;
        if (std::strcmp(argv[i], "--boss-rush") == 0) {
            opt.questCfg.scenario = MapScenario::BOSS_RUSH;
            opt.questCfg.enabled = true;
            opt.questCfg.autoPatrol = true;
        }
        if (std::strcmp(argv[i], "--atlas-quest") == 0) {
            opt.questCfg.scenario = MapScenario::ATLAS_QUEST_RUSH;
            opt.questCfg.enabled = true;
            opt.questCfg.autoPatrol = true;
        }
        if (std::strcmp(argv[i], "--fast-clear") == 0) {
            opt.questCfg.scenario = MapScenario::FAST_CLEAR;
            opt.questCfg.enabled = true;
            opt.questCfg.autoPatrol = true;
        }
        if (std::strcmp(argv[i], "--full-clear") == 0) {
            opt.questCfg.scenario = MapScenario::FULL_CLEAR;
            opt.questCfg.enabled = true;
            opt.questCfg.autoPatrol = true;
        }
        if (std::strcmp(argv[i], "--map-scenario") == 0 && i + 1 < argc) {
            std::string scStr = argv[++i];
            for (char& c : scStr) c = static_cast<char>(::tolower(static_cast<unsigned char>(c)));
            if (scStr == "boss_rush" || scStr == "boss" || scStr == "rush") {
                opt.questCfg.scenario = MapScenario::BOSS_RUSH;
            } else if (scStr == "atlas_quest" || scStr == "quest" || scStr == "citadel") {
                opt.questCfg.scenario = MapScenario::ATLAS_QUEST_RUSH;
            } else if (scStr == "fast_clear" || scStr == "fast") {
                opt.questCfg.scenario = MapScenario::FAST_CLEAR;
            } else if (scStr == "full_clear" || scStr == "full") {
                opt.questCfg.scenario = MapScenario::FULL_CLEAR;
            }
            opt.questCfg.enabled = true;
            opt.questCfg.autoPatrol = true;
        }
        if (std::strcmp(argv[i], "--auto-portal-exit") == 0 || std::strcmp(argv[i], "--auto-portal") == 0) {
            opt.questCfg.autoPortalAfterBoss = true;
        }
        if (std::strcmp(argv[i], "--no-auto-portal") == 0) {
            opt.questCfg.autoPortalAfterBoss = false;
        }
        if (std::strcmp(argv[i], "--pulse-e") == 0) {
            opt.questCfg.pulseEWithForward = true;
        }
        if (std::strcmp(argv[i], "--no-pulse-e") == 0) {
            opt.questCfg.pulseEWithForward = false;
        }
        if (std::strcmp(argv[i], "--portal-vk") == 0 && i + 1 < argc) {
            uint8_t vk = static_cast<uint8_t>(std::strtoul(argv[++i], nullptr, 0));
            opt.questCfg.portalVk = vk;
            opt.reflexCfg.portalVk = vk;
            uint8_t hid = (vk == 0xBE) ? 0x37 : ((vk == 0x54) ? 0x17 : ((vk >= 'A' && vk <= 'Z') ? static_cast<uint8_t>(0x04 + (vk - 'A')) : ((vk >= '1' && vk <= '9') ? static_cast<uint8_t>(0x1E + (vk - '1')) : 0x37)));
            opt.questCfg.portalHid = hid;
            opt.reflexCfg.portalHid = hid;
        }
        if (std::strcmp(argv[i], "--max-mana") == 0 && i + 1 < argc)
            opt.expectedMaxMana = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--max-hp") == 0 && i + 1 < argc)
            opt.expectedMaxHP = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--ci") == 0 || std::strcmp(argv[i], "--chaos-inoculation") == 0)
            opt.expectedMaxHP = 1;
        if (std::strcmp(argv[i], "--ci-staff") == 0 || std::strcmp(argv[i], "--ci-charged-staff") == 0) {
            opt.expectedMaxHP = 1;
            opt.comboCfg.isCiChargedStaffMode = true;
            opt.comboCfg.primarySkillVk = 'F'; // 0x46 - Chiêu chính chuyển sang phím F
            opt.comboCfg.staffMeleeSkillVk = 'F';
            opt.comboCfg.staffBuffSkillVk = VK_RBUTTON;
            opt.comboCfg.isRangedMode = false;
            opt.questCfg.combatStopDistance = 220.0f;
            opt.questCfg.isRanged = false;
            opt.reflexCfg.manaThreshold = 0.50f;
            opt.reflexCfg.ciManaFlaskThreshold = 0.50f;
        }
        if (std::strcmp(argv[i], "--max-es") == 0 && i + 1 < argc)
            opt.expectedMaxES = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--max-ward") == 0 && i + 1 < argc)
            opt.expectedMaxWard = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--ward-addr") == 0 && i + 1 < argc)
            opt.wardAddrCmd = std::strtoull(argv[++i], nullptr, 16);
        if (std::strcmp(argv[i], "--auto-respawn") == 0) opt.reflexCfg.autoRespawn = true;
        if (std::strcmp(argv[i], "--no-respawn") == 0) opt.reflexCfg.autoRespawn = false;
        if (std::strcmp(argv[i], "--respawn-delay") == 0 && i + 1 < argc)
            opt.reflexCfg.respawnDelayMs = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--mana-addr") == 0 && i + 1 < argc)
            opt.manaAddrCmd = std::strtoull(argv[++i], nullptr, 16);
        if (std::strcmp(argv[i], "--entity-offset") == 0 && i + 1 < argc) {
            opt.entityOffsetCmd = std::strtoull(argv[++i], nullptr, 16);
        }
        if (std::strcmp(argv[i], "--autologin") == 0) opt.autoLoginCfg.enabled = true;
        if (std::strcmp(argv[i], "--no-autologin") == 0) opt.autoLoginCfg.enabled = false;
        if (std::strcmp(argv[i], "--login-delay") == 0 && i + 1 < argc)
            opt.autoLoginCfg.reconnectDelayMs = static_cast<uint32_t>(std::strtoul(argv[++i], nullptr, 10));
        if (std::strcmp(argv[i], "--login-pass") == 0 && i + 1 < argc)
            opt.autoLoginCfg.password = argv[++i];
        if ((std::strcmp(argv[i], "--char-slot") == 0 ||
             std::strcmp(argv[i], "--character") == 0 ||
             std::strcmp(argv[i], "--char") == 0) && i + 1 < argc) {
            std::string val = argv[++i];
            if (!val.empty() && std::all_of(val.begin(), val.end(), ::isdigit)) {
                opt.autoLoginCfg.charSlot = static_cast<uint32_t>(std::strtoul(val.c_str(), nullptr, 10));
            }
            opt.autoLoginCfg.characterName = val;
        }
        if ((std::strcmp(argv[i], "--target-zone") == 0 || std::strcmp(argv[i], "--zone") == 0) && i + 1 < argc) {
            std::string tz = argv[++i];
            opt.questCfg.targetZone = tz;
        }
        if (std::strcmp(argv[i], "--league") == 0 && i + 1 < argc)
            opt.autoLoginCfg.leagueName = argv[++i];
    }

    opt.reflexCfg.moveMode = opt.moveMode;
    opt.lootCfg.moveMode = opt.moveMode;
    opt.questCfg.moveMode = opt.moveMode;

    if (opt.comboCfg.enabled || opt.lootCfg.enabled || opt.questCfg.enabled) {
        opt.allowPhysicalMove = true;
    }

    return opt;
}

} // namespace poe2::cli
