#include "options.h"

namespace NSaasLB {
    void IOptions::Init() {
        AddToParsing();
    }

    void IOptions::Parse(const TString& program, int argc, char* argv[]) {
        Parser.SetCustomUsage(program + " ");
        ParsingResult.emplace(&Parser, argc, argv);

        InitGlobalLog();
    }

    void IOptions::PrintUsage(const TString& program) const {
        Parser.PrintUsage(program, Cout);
    }

    void IOptions::AddToParsing() {
        Parser.AddHelpOption();
        Parser.AddVersionOption();
        Parser.SetFreeArgsNum(0);

        Parser.AddLongOption("log", "path to log, 'null' means to not write log")
            .RequiredArgument("<log path>")
            .DefaultValue(GetHomeDir() + "/.saas/lb_tool.log")
            .StoreResult(&GlobalLog);

        Parser.AddLongOption("log-level", "log level")
            .RequiredArgument("<log level>")
            .DefaultValue(TLOG_DEBUG)
            .StoreResult(&LogLevel);
    }

    void IOptions::InitGlobalLog() {
        DoInitGlobalLog(GlobalLog, LogLevel, false, false);
    }

    bool TModifyOptions::GetDryRun() const {
        return ParsingResult->Has("dry-run");
    }

    bool TModifyOptions::GetSkipConfirm() const {
        return ParsingResult->Has("skip-confirm");
    }

    void TModifyOptions::AddToParsing() {
        IOptions::AddToParsing();

        DoAddToParsing();
    }

    void TModifyOptions::DoAddToParsing() {
        Parser.AddLongOption("dry-run", "dry run")
            .NoArgument();
        Parser.AddLongOption('y', "skip-confirm", "skip user confirm and apply changes")
            .NoArgument();
    }

    TString TNamespaceOptions::GetName() const {
        return Name;
    }

    void TNamespaceOptions::AddToParsing() {
        IOptions::AddToParsing();

        DoAddToParsing();
    }

    void TNamespaceOptions::DoAddToParsing() {
        Parser.AddLongOption('n', "namespace", "namespace name")
            .Required()
            .RequiredArgument("<name>")
            .StoreResult(&Name);
    }

    void TCommonModifyNamespaceOptions::AddToParsing() {
        TBase::AddToParsing();

        TNamespaceOptions::DoAddToParsing();
        TModifyOptions::DoAddToParsing();
    }

    TString TModifyNamespaceOptions::GetLogbrokerServicesPath() const {
        return LogbrokerPath;
    }

    TString TModifyNamespaceOptions::GetLocksServer() const {
        return LocksServer;
    }

    TString TModifyNamespaceOptions::GetLocksPath() const {
        return LocksPath;
    }

    std::optional<TString> TModifyNamespaceOptions::GetServicesConfigsPath() const {
        if (ParsingResult->Has("configs-path")) {
            return ParsingResult->Get("configs-path");
        }
        return {};
    }

    std::optional<TString> TModifyNamespaceOptions::GetDeployManagerHost() const {
        if (ParsingResult->Has("dm-host")) {
            return ParsingResult->Get("dm-host");
        }
        return {};
    }

    std::optional<TString> TModifyNamespaceOptions::GetDevConsumersPath() const {
        if (ParsingResult->Has("dev-consumers")) {
            return ParsingResult->Get("dev-consumers");
        }
        return {};
    }

    void TModifyNamespaceOptions::AddToParsing() {
        TCommonModifyNamespaceOptions::AddToParsing();

        Parser.AddLongOption("logbroker-path", "logbroker services path")
            .Required()
            .RequiredArgument("<path>")
            .StoreResult(&LogbrokerPath);
        Parser.AddLongOption("locks-server", "ZooKeeper locks servers")
            .Required()
            .RequiredArgument("<servers>")
            .StoreResult(&LocksServer);
        Parser.AddLongOption("locks-path", "locks path in ZooKeeper")
            .Required()
            .RequiredArgument("<path>")
            .StoreResult(&LocksPath);
        Parser.AddLongOption("configs-path", "services configs path in ZooKeeper")
            .Optional()
            .RequiredArgument("<path>");
        Parser.AddLongOption("dm-host", "deploy_manager host")
            .Optional()
            .RequiredArgument("<dm-host>");
        Parser.AddLongOption("dev-consumers", "path for developers consumers")
            .Optional()
            .RequiredArgument("<path>");
    }

    TString TServiceOptions::GetNamespace() const {
        return Namespace;
    }

    TString TServiceOptions::GetService() const {
        return Service;
    }

    TString TServiceOptions::GetCtype() const {
        return Ctype;
    }

    std::optional<TString> TServiceOptions::GetGencfgTag() const {
        if (ParsingResult->Has("gencfg-tag")) {
            return ParsingResult->Get("gencfg-tag");
        }
        return {};
    }

    void TServiceOptions::AddToParsing() {
        IOptions::AddToParsing();

        DoAddToParsing();
    }

    void TServiceOptions::DoAddToParsing() {
        Parser.AddLongOption('n', "namespace", "service namespace")
            .Required()
            .RequiredArgument("<namespace>")
            .StoreResult(&Namespace);
        Parser.AddLongOption('s', "service", "service name")
            .Required()
            .RequiredArgument("<service>")
            .StoreResult(&Service);
        Parser.AddLongOption('c', "ctype", "service ctype")
            .Required()
            .RequiredArgument("<ctype>")
            .StoreResult(&Ctype);
        Parser.AddLongOption("gencfg-tag", "gencfg tag")
            .Optional()
            .RequiredArgument("<gencfg-tag>");
    }

    bool TDescribeServiceOptions::GetShowLocks() const {
        return ParsingResult->Has("locks");
    }

    void TDescribeServiceOptions::AddToParsing() {
        TServiceOptions::AddToParsing();

        Parser.AddLongOption("locks", "show active locks")
            .NoArgument();
    }

    bool TModifyServiceOptions::GetNoRemove() const {
        return ParsingResult->Has("no-remove");
    }

    bool TModifyServiceOptions::GetForceRemove() const {
        return ParsingResult->Has("force-remove");
    }

    void TModifyServiceOptions::AddToParsing() {
        TBase::AddToParsing();

        TServiceOptions::DoAddToParsing();
        TModifyOptions::DoAddToParsing();

        Parser.AddLongOption("no-remove", "no remove any items")
            .NoArgument();
        Parser.AddLongOption("force-remove", "delete resources even if they are used by another service")
            .NoArgument();
    }

    std::optional<TVector<TString>> TUpdateTopicsOptions::GetShards() const {
        if (ParsingResult->Has("shards")) {
            TString shardsString = ParsingResult->Get("shards");
            return SplitString(shardsString, ",");
        }
        return {};
    }

    void TUpdateTopicsOptions::AddToParsing() {
        TModifyServiceOptions::AddToParsing();

        DoAddToParsing();
    }

    void TUpdateTopicsOptions::DoAddToParsing() {
        Parser.AddLongOption("shards", "shards list")
            .RequiredArgument("<shards>");
    }

    std::optional<ui32> TUpdateConsumersOptions::GetReplicas() const {
        if (ParsingResult->Has("replicas")) {
            return FromString<ui32>(ParsingResult->Get("replicas"));
        }
        return {};
    }

    void TUpdateConsumersOptions::AddToParsing() {
        TModifyServiceOptions::AddToParsing();

        DoAddToParsing();
    }

    void TUpdateConsumersOptions::DoAddToParsing() {
        Parser.AddLongOption("replicas", "total number of replicas in all datacenters")
            .RequiredArgument("<replicas>");
    }

    ELogbrokerName TCreateServiceOptions::GetLogbrokerName() const {
        TString lbEnumName = ParsingResult->Get("lb");
        std::replace(lbEnumName.begin(), lbEnumName.vend(), '-', '_');
        return FromString<NSaasLB::ELogbrokerName>(lbEnumName);
    }

    TVector<ui32> TCreateServiceOptions::GetTvmWrite() const {
        TVector<ui32> ids;
        if (ParsingResult->Has("tvm-write")) {
            TString idsString = ParsingResult->Get("tvm-write");
            for (auto& id : SplitString(idsString, ",")) {
                ids.push_back(FromString<ui32>(id));
            }
        }
        return ids;
    }

    TVector<ui32> TCreateServiceOptions::GetTvmRead() const {
        TVector<ui32> ids;
        if (ParsingResult->Has("tvm-read")) {
            TString idsString = ParsingResult->Get("tvm-read");
            for (auto& id : SplitString(idsString, ",")) {
                ids.push_back(FromString<ui32>(id));
            }
        }
        return ids;
    }

    std::optional<TString> TCreateServiceOptions::GetLocksPath() const {
        if (ParsingResult->Has("locks-path")) {
            return ParsingResult->Get("locks-path");
        }
        return {};
    }

    std::optional<TString> TCreateServiceOptions::GetTopicsPath() const {
        if (ParsingResult->Has("topics-path")) {
            return ParsingResult->Get("topics-path");
        }
        return {};
    }

    std::optional<TString> TCreateServiceOptions::GetConsumersPath() const {
        if (ParsingResult->Has("consumers-path")) {
            return ParsingResult->Get("consumers-path");
        }
        return {};
    }

    TVector<EDataCenter> TCreateServiceOptions::GetDataCenters() const {
        TVector<EDataCenter> result;
        auto dcList = SplitString(ParsingResult->Get("data-centers"), ",");
        for (auto dc : dcList) {
            result.push_back(FromString<EDataCenter>(dc));
        }
        return result;
    }

    std::optional<TVector<TString>> TCreateServiceOptions::GetGencfgGroups() const {
        if (ParsingResult->Has("gencfg-groups")) {
            return SplitString(ParsingResult->Get("gencfg-groups"), ",");
        }
        return {};
    }

    TVector<TString> TCreateServiceOptions::GetYtDeliveryClusters() const {
        if (ParsingResult->Has("yt-clusters")) {
            return SplitString(ParsingResult->Get("yt-clusters"), ",");
        }
        return {};
    }

    std::optional<TString> TCreateServiceOptions::GetDeployManagerHost() const {
        if (ParsingResult->Has("dm-host")) {
            return ParsingResult->Get("dm-host");
        }
        return {};
    }

    std::optional<ELogbrokerName> TCreateServiceOptions::GetLogbrokerMirrorName() const {
        if (ParsingResult->Has("lb-mirror")) {
            TString lbEnumMirrorName = ParsingResult->Get("lb-mirror");
            std::replace(lbEnumMirrorName.begin(), lbEnumMirrorName.vend(), '-', '_');
            return FromString<NSaasLB::ELogbrokerName>(lbEnumMirrorName);
        }
        return {};
    }

    std::optional<TString> TCreateServiceOptions::GetMirrorTopicsPath() const {
        if (ParsingResult->Has("mirror-topics-path")) {
            return ParsingResult->Get("mirror-topics-path");
        }
        return {};
    }

    std::optional<TString> TCreateServiceOptions::GetMirrorConsumersPath() const {
        if (ParsingResult->Has("mirror-consumers-path")) {
            return ParsingResult->Get("mirror-consumers-path");
        }
        return {};
    }

    void TCreateServiceOptions::AddToParsing() {
        TBase::AddToParsing();

        TUpdateConsumersOptions::DoAddToParsing();
        TUpdateTopicsOptions::DoAddToParsing();

        Parser.AddLongOption("lb", "logbroker cluster name")
            .Required()
            .RequiredArgument("<logbroker cluster>");
        Parser.AddLongOption("tvm-write", "comma separated user tvm ids for writing to topics")
            .Optional()
            .RequiredArgument("<tvm_id>");
        Parser.AddLongOption("tvm-read", "comma separated user tvm ids for reading topics")
            .Optional()
            .RequiredArgument("<tvm_id>");
        Parser.AddLongOption("locks-path", "path prefix for locks without service specific")
            .Optional()
            .RequiredArgument("<path>");
        Parser.AddLongOption("topics-path", "path for topics in logbroker")
            .Optional()
            .RequiredArgument("<path>");
        Parser.AddLongOption("consumers-path", "path for consumers in logbroker")
            .Optional()
            .RequiredArgument("<path>");
        Parser.AddLongOption("data-centers", "comma separated logbroker data centers list")
            .Required()
            .RequiredArgument("<dc list>");
        Parser.AddLongOption("gencfg-groups", "comma separated gencfg groups list")
            .Optional()
            .RequiredArgument("<gencfg groups list>");
        Parser.AddLongOption("dm-host", "deploy_manager host")
            .Optional()
            .RequiredArgument("<dm host>");
        Parser.AddLongOption("yt-clusters", "comma separated yt delivery clusters list")
            .Optional()
            .RequiredArgument("<yt delivery clusters list>");

        Parser.AddLongOption("lb-mirror", "logbroker cluster name to which topics will be mirrored")
            .Optional()
            .RequiredArgument("<logbroker cluster>");
        Parser.AddLongOption("mirror-topics-path", "topics' path in logbroker mirror cluster")
            .Optional()
            .RequiredArgument("<path>");
        Parser.AddLongOption("mirror-consumers-path", "consumers' path in logbroker mirror cluster")
            .Optional()
            .RequiredArgument("<path>");
    }

    TVector<ui32> TModifyPermissionsOptions::GetTvmIds() const {
        TString idsString = ParsingResult->Get("tvm");
        TVector<ui32> ids;
        for (auto& id : SplitString(idsString, ",")) {
            ids.push_back(FromString<ui32>(id));
        }
        return ids;
    }

    void TModifyPermissionsOptions::AddToParsing() {
        TModifyServiceOptions::AddToParsing();

        Parser.AddLongOption("tvm", "comma separated tvm ids list")
            .Required()
            .RequiredArgument("<tvm_id>");
    }
}
