#include "ascii_art.h"
#include "parsers.h"
#include "commands.h"

#include <util/string/join.h>

namespace {
    bool HasHelpArgument(ui32 argc, char* argv[]) {
        for (ui32 i = 0; i < argc; ++i) {
            TString arg(argv[i]);
            if (arg == "--help" || arg == "-?") {
                return true;
            }
        }
        return false;
    }
}

namespace NSaasLB {
    void ICommandParser::AddParserChild(TVector<TString>::iterator begin, TVector<TString>::iterator end, ICommandParser::TPtr) {
        ythrow yexception() << "Init utility error: can not add command with suffix=" << JoinRange(" ", begin, end);
    }

    ICommand::TPtr TMultiCommandParser::Parse(int index, int argc, char* argv[]) const {
        if (index < argc && Childs.count(argv[index])) {
            return Childs.at(argv[index])->Parse(index + 1, argc, argv);
        }
        TString error = "";
        if (index < argc && !HasHelpArgument(argc, argv)) {
            error = Join("'", "unknown child command ", argv[index], "");
        }
        TString commandLine = JoinRange(" ", argv, argv + index);
        PrintHelp(commandLine, error);
        return nullptr;
    }

    void TMultiCommandParser::PrintHelp(const TString& commandLine, const TString& error /*= ""*/) const {
        if (error) {
            Cout << "Incorrect usage: " << error << Endl;
        }
        Cout << "Childs commands for " << commandLine << Endl;
        PrintChildsCommands("");
    }

    void TMultiCommandParser::AddParserChild(TVector<TString>::iterator begin, TVector<TString>::iterator end, ICommandParser::TPtr parser) {
        TStringBuf nextPartName = *begin;
        auto nextPart = Childs[nextPartName];
        if (begin + 1 == end) {
            if (!nextPart) {
                nextPart = parser;
                Childs[nextPartName] = nextPart;
            }
        } else {
            if (!nextPart) {
                nextPart = MakeAtomicShared<TMultiCommandParser>();
                Childs[nextPartName] = nextPart;
            }
            nextPart->AddParserChild(begin + 1, end, parser);
        }
    }

    void TMultiCommandParser::PrintChildsCommands(const TString& outputLinePrefix) const {
        for (auto it = Childs.begin(); it != Childs.end(); ++it) {
            bool last = NAsciiArt::isLastItem(Childs.end(), it);
            Cout << outputLinePrefix << (last ? NAsciiArt::LIST_ITEM_LAST : NAsciiArt::LIST_ITEM) << it->first << Endl;
            it->second->PrintChildsCommands(outputLinePrefix + (last ? NAsciiArt::SPACE : NAsciiArt::LIST));
        }
    }

    template <class TCommand>
    ICommand::TPtr TFinalCommandParser<TCommand>::Parse(int index, int argc, char* argv[]) const {
        TString commandLine = JoinRange(" ", argv, argv + index);
        TOptions options;
        options.Init();
        try {
            options.Parse(commandLine, argc - index + 1, argv + index - 1);

            return MakeAtomicShared<TCommand>(options);
        } catch (NLastGetopt::TUsageException& e) {
            PrintHelp(options, commandLine, e.what());
        }
        return nullptr;
    }

    template <class TCommand>
    void TFinalCommandParser<TCommand>::PrintHelp(const TOptions& options, const TString& commandLine, const TString& error /*= ""*/) const {
        if (error) {
            Cout << "Incorrect usage: " << error << Endl;
        }
        options.PrintUsage(commandLine);
    }

    TMainCommandParser::TMainCommandParser() {
        Init();
    }

    void TMainCommandParser::Init() {
        AddCommand<TUpdateConsumersCommand>("update consumers");
        AddCommand<TUpdateTopicsCommand>("update topics");
        AddCommand<TUpdateLocksCommand>("update locks");
        AddCommand<TCreateServiceCommand>("create service");
        AddCommand<TCreateNamespaceCommand>("create namespace");
        AddCommand<TUpdateNamespaceCommand>("update namespace");
        AddCommand<TRemoveNamespaceCommand>("remove namespace");
        AddCommand<TRemoveServiceCommand>("remove service");
        AddCommand<TDescribeServiceCommand>("describe service");
        AddCommand<TDescribeNamespaceCommand>("describe namespace");
        AddCommand<TModifyPermissionsCommand<EPermissionModiftyType::GRANT, EServicePermission::read>>("grant read");
        AddCommand<TModifyPermissionsCommand<EPermissionModiftyType::GRANT, EServicePermission::write>>("grant write");
        AddCommand<TModifyPermissionsCommand<EPermissionModiftyType::REVOKE, EServicePermission::read>>("revoke read");
        AddCommand<TModifyPermissionsCommand<EPermissionModiftyType::REVOKE, EServicePermission::write>>("revoke write");
    }
}
