#pragma once

#include <map>
#include <set>
#include <string>
#include <type_traits>
#include <vector>


class CommandLine;

enum class CommandLineValueType {
    Boolean = 0,
    Int = 1,
    String = 2,
};

struct CommandLineOption {
    std::string Value;
    std::string Description;
    CommandLineValueType Type;
    bool HasOption;
};

class CommandLineTargetOptions {
public:
    CommandLineTargetOptions();

    /**
     * @brief 添加解析的字符串参数
     */
    template<typename T>
    CommandLineTargetOptions &Add(const std::string_view name, std::string_view shortName,
                                  std::string_view description) {
        CommandLineValueType type = CommandLineValueType::Boolean;
        if constexpr (std::is_same_v<T, bool>) {
            type = CommandLineValueType::Boolean;
        } else if constexpr (std::is_same_v<T, int>) {
            type = CommandLineValueType::Int;
        } else if constexpr (std::is_same_v<T, std::string>) {
            type = CommandLineValueType::String;
        } else {
            return *this;
        }

        if (!shortName.empty()) {
            _shortMap.insert({std::string(shortName), std::string(name)});
        }
        _args.insert({
                std::string(name),
                CommandLineOption{"", std::string(description), type, false}
        });
        return *this;
    }

    CommandLineTargetOptions &EnableKeyValueArgs();

private:
    template<typename T>
    T Get(std::string_view name) {
        if (!HasOption(name)) {
            return T();
        }

        auto it = _args.find(name);
        auto &arg = it->second;
        if constexpr (std::is_same_v<T, bool>) {
            return arg.Value == "true";
        } else if constexpr (std::is_same_v<T, int>) {
            return std::stoi(arg.Value);
        } else if constexpr (std::is_same_v<T, std::string>) {
            return arg.Value;
        }

        return {};
    }

    bool HasOption(std::string_view name);

    friend CommandLine;
    std::map<std::string, CommandLineOption, std::less<>> _args;
    std::map<std::string, std::string, std::less<>> _shortMap;
    std::map<std::string, std::string, std::less<>> _restArgs;
    bool _enableRestArgs;
};

/**
 * @brief 特定语法结构的命令行解析工具
 */
class CommandLine {
public:
    CommandLine();

    template<typename T>
    T Get(std::string_view name) {
        return _options->Get<T>(name);
    }

    bool HasOption(std::string_view name);

    /**
     * @brief 添加解析目标
     */
    CommandLineTargetOptions &AddTarget(std::string_view name);

    std::string_view GetTarget() const noexcept;

    /**
     * @brief 按顺序获取参数
     */
    std::string_view GetArg(int index) const noexcept;

    std::map<std::string, std::string, std::less<>> &GetKeyValueOptions();

    bool Parse(int argc, char **argv);

    void SetUsage(std::string_view usage);

    void PrintUsage();

private:
    std::vector<std::string> _argvs;
    std::map<std::string, CommandLineTargetOptions, std::less<>> _targets;
    CommandLineTargetOptions *_options;
    std::string _currentTarget;
    std::vector<std::string> _errors;
    std::string _usage;
};
