#include <CLI11.hpp>
#include <iostream>
#include <of_util.h>
#include <regex>

#include "client.h"
#include "config.h"
#include "version.h"

#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#include <windows.h>
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
#endif

#if defined(GRAB_CRASH)
#ifdef USE_BOOST
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#endif
#include <crashelper.h>
#endif

std::shared_ptr<ClientConfig> g_Config = nullptr;
int parse_cmd(int argc, char** argv, CmdParam& param)
{
    std::string intro(fmt::format("tsc cmd introduce, version: {}\nopensource: {}", VERSION_NUM, VERSION_URL));
    CLI::App app(intro);

    app.add_option("-u, --use", param.use_config, "使用服务器地址组(值为使用--show中显示的序号)");
    app.add_option("-a, --append", param.appendValue, "添加服务器地址组(地址格式:127.0.0.1:9898:注释)");
    app.add_flag("-s, --show", param.showValue, "查看服务器地址组");
    app.add_option("-r, --remove", param.removeValue, "移除服务器地址组(值为使用--show中显示的序号)");
    app.add_flag("-d, --direct", param.direct_use, "添加服务器时直接使用此服务器。");
    app.add_flag("-l, --last", param.last_use, "直接使用之前最后一次使用的服务器。");
    app.add_flag("-n, --null", param.null_use, "先运行在选择服务器。");
    app.add_option("-c, --connect", param.connectValue, "直连服务器((地址格式:127.0.0.1:9898)。");

    if (argc == 1) {
        std::cout << app.help() << std::endl;
        return 0;
    }
    // 这里的 CLI11_PARSE 在程序没有输入或者仅输入--help(-h)时,会直接返回,后面代码都不会执行。
    // 当有自定义的参数被输入时,后面代码会执行。
    try {
        CLI11_PARSE(app, argc, argv);
    } catch (const CLI::ParseError& e) {
        return app.exit(e);
    }
    param.parsed = true;
    return 0;
}

bool select_server(const std::vector<TransmSet>& sets, std::string& ip, long& port)
{
    TLOGI("Please Select a Server:");
    if (sets.empty()) {
        TLOGE("No servers available to select.");
        return false;
    }
    // 打印所有服务器选项(带编号)
    for (size_t i = 0; i < sets.size(); ++i) {
        const auto& server = sets[i];
        if (server.comment.empty()) {
            TLOGI("[{}] {}:{}", i + 1, server.ip, server.port);
        } else {
            TLOGI("[{}] {}:{} ({})", i + 1, server.ip, server.port, server.comment);
        }
    }

    while (true) {
        TLOGW("Enter server number (or 'exit' to cancel): ");
        std::string input;
        std::getline(std::cin, input);

        // 检查是否输入了退出命令
        if (input == "exit" || input == "quit" || input == "q") {
            TLOGD("User canceled server selection.");
            return false;
        }

        // 检查输入是否为空或非数字
        if (input.empty() || !std::all_of(input.begin(), input.end(), ::isdigit)) {
            TLOGE("Invalid input '{}'. Please enter a valid number or 'exit'.", input);
            continue;
        }

        // 转换为数字
        int choice = std::stoi(input);

        // 检查数字是否在有效范围内
        if (choice < 1 || choice > static_cast<int>(sets.size())) {
            TLOGE("Invalid choice '{}'. Please select a number between 1 and {}.", choice, sets.size());
            continue;
        }

        // 选择成功,返回对应的 IP 和端口
        const auto& selected = sets[choice - 1];
        ip = selected.ip;
        port = selected.port;
        TLOGI("Selected server: {}:{} ({})", ip, port, selected.comment);
        return true;
    }
}

bool exec_cmd(CmdParam& param, bool& run)
{
    run = false;
    // 如果是展示
    if (param.showValue) {
        std::vector<TransmSet> set;
        if (!g_Config->read_ini(set)) {
            return false;
        }
        for (const auto& item : set) {
            TLOGI("{} => {}:{} {}", item.group, item.ip, item.port, item.comment);
        }
        return true;
    }
    if (param.use_config != -1) {
        run = true;
        return true;
    }
    if (!param.appendValue.empty() && !param.removeValue.empty()) {
        TLOGW("append and remove can't simultaneous operate!");
        return false;
    }
    if (!param.appendValue.empty()) {
        std::regex pattern(R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+)(?::(.*))?)");
        std::smatch matches;
        std::string ip, port, comment;
        if (std::regex_match(param.appendValue, matches, pattern)) {
            ip = matches[1].str();
            port = matches[2].str();
            comment = matches.size() > 3 ? matches[3].str() : "default";
        }
        auto id = g_Config->append_ini(ip, std::stol(port), comment);
        if (id < 0) {
            TLOGW("add {}:{} failed.", ip, port);
            return false;
        }
        TLOGI("add {}:{} success.", ip, port);
        param.use_config = id;
        return true;
    }
    if (!param.removeValue.empty()) {
        if (!g_Config->remove_ini(std::stol(param.removeValue))) {
            TLOGW("remove config num=[{}] failed, please check!", param.removeValue);
            return false;
        }
        TLOGI("remove config num=[{}] success!", param.removeValue);
        return true;
    }
    if (param.last_use || param.null_use) {
        return true;
    }
    TLOGW("not matched!", param.removeValue);
    return false;
}

int main(int argc, char* argv[])
{
#if defined(GRAB_CRASH)
    auto config_dir = COfPath::get_config_dir("transm", true);
    auto err = fs::path(config_dir).append("errs").string();
    backward::SetDumpFileSavePath(err);
    backward::SetDumpLogSavePath(err);
    CRASHELPER_MARK_ENTRY();
#endif

#ifdef _WIN32
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD mode;
    GetConsoleMode(hConsole, &mode);
    mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    SetConsoleMode(hConsole, mode);
#endif

    std::shared_ptr<int> deleter(new int(), [](int* p) {
        fc_recovery_color();
        delete p;
    });

    g_Config = std::make_shared<ClientConfig>();
    if (!g_Config->baseInit()) {
        return -1;
    }

    bool run = false;
    CmdParam param;
    parse_cmd(argc, argv, param);
    if (!param.parsed) {
        return 0;
    }

    std::string ip;
    long port{};

    if (!param.connectValue.empty()) {
        std::regex pattern(R"((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):(\d+))");
        std::smatch matches;

        if (std::regex_match(param.connectValue, matches, pattern) && matches.size() == 3) {
            ip = matches[1].str();
            port = std::stol(matches[2].str());
            run = true;
        } else {
            TLOGE("Invalid connect value [{}]", param.connectValue);
            return -1;
        }
    } else if (!exec_cmd(param, run)) {
        TLOGW("exec_cmd failed!");
        return -1;
    }

    if (!run && !param.direct_use && !param.last_use && !param.null_use) {
        return 0;
    }

    if (ip.empty()) {
        std::vector<TransmSet> sets;
        if (!g_Config->read_ini(sets)) {
            return -1;
        }

        if (param.null_use) {
            if (!select_server(sets, ip, port)) {
                return -1;
            }
        } else {
            if (param.last_use) {
                if (!g_Config->get_last_use(ip, port)) {
                    return -1;
                }
            } else {
                TransmSet use;
                if (!g_Config->get_ini(sets, param.use_config, use)) {
                    TLOGW("Not found config by num:[{}]", param.use_config);
                    return -1;
                }
                ip = use.ip;
                port = use.port;
            }
        }
        g_Config->save_last_use(ip, port);
    }
    TLOGI("Build At {} {} under {} on {}", __DATE__, __TIME__, VERSION_GIT_COMMIT, VERSION_GIT_BRANCH);
    TLOGI("use ip => [{}], port => [{}]", ip, port);
    std::shared_ptr<TransmClient> client = std::make_shared<TransmClient>();
    client->run(ip, std::to_string(port), g_Config->get_config_dir());
    TLOGI("exit ==========");
    return 0;
}