#include "remote_mirror_rules.h"

#include <saas/library/persqueue/common/common.h>
#include <saas/library/persqueue/common/installations.h>

namespace NSaasLB {
    TRemoteMirrorRule::TRemoteMirrorRule(
        const TString& topic,
        EDataCenter dataCenter,
        const TString& originTopic,
        const TString& originConsumer,
        ELogbrokerName originLogbroker
    )
        : Topic(GetCorrectEntityPath(topic))
        , DataCenter(dataCenter)
        , OriginTopic(GetCorrectEntityPath(originTopic))
        , OriginConsumer(GetCorrectEntityPath(originConsumer))
        , OriginLogbroker(originLogbroker)
    {}

    TRemoteMirrorRule::TRemoteMirrorRule(const NLogBroker::RemoteMirrorRuleAdmin& remoteMirrorRule) {
        Topic = remoteMirrorRule.remote_mirror_rule().topic().path();
        DataCenter = FromString(remoteMirrorRule.remote_mirror_rule().cluster().cluster());

        OriginTopic = GetPropertyValue<TString>(remoteMirrorRule.properties().src_topic());
        OriginConsumer = GetPropertyValue<TString>(remoteMirrorRule.properties().src_consumer());
        TString endpoint = GetPropertyValue<TString>(remoteMirrorRule.properties().src_cluster_endpoint());

        OriginLogbroker = GetLogbrokerNameByEndpointWithPort(endpoint);
    }

    TRemoteMirrorRuleKey TRemoteMirrorRule::GetKey() const {
        return std::tie(Topic, DataCenter, OriginTopic, OriginConsumer, OriginLogbroker);
    }

    TString TRemoteMirrorRule::GetTopicPath() const {
        return Topic;
    }

    EDataCenter TRemoteMirrorRule::GetDataCenter() const {
        return DataCenter;
    }

    NLogBroker::SingleModifyRequest TRemoteMirrorRule::GetAddCommand(const TString& oAuthToken) const {
        NLogBroker::SingleModifyRequest modifyRequest;
        auto& ruleRequest = *modifyRequest.mutable_create_remote_mirror_rule();
        FillRuleRequestKey(*ruleRequest.mutable_remote_mirror_rule());
        FillRuleRequestProperties(*ruleRequest.mutable_properties());
        ruleRequest.mutable_properties()->mutable_credentials()->set_oauth_token(oAuthToken);
        return modifyRequest;
    }

    NLogBroker::SingleModifyRequest TRemoteMirrorRule::GetRemoveCommand() const {
        NLogBroker::SingleModifyRequest modifyRequest;
        auto& ruleRequest = *modifyRequest.mutable_remove_remote_mirror_rule();
        FillRuleRequestKey(*ruleRequest.mutable_remote_mirror_rule());
        return modifyRequest;
    }

    bool TRemoteMirrorRule::operator== (const TRemoteMirrorRule &other) const {
        return GetKey() == other.GetKey();
    }

    bool TRemoteMirrorRule::operator< (const TRemoteMirrorRule& other) const {
        return GetKey() < other.GetKey();
    }

    void TRemoteMirrorRule::FillRuleRequestKey(NLogBroker::RemoteMirrorRuleKey& key) const {
        key.mutable_topic()->set_path(Topic);
        key.mutable_cluster()->set_cluster(ToString(DataCenter));
    }

    void TRemoteMirrorRule::FillRuleRequestProperties(NLogBroker::RemoteMirrorRuleProperties &props) const {
        props.mutable_src_topic()->set_user_defined(OriginTopic);
        props.mutable_src_consumer()->set_user_defined(OriginConsumer);
        props.mutable_src_cluster_endpoint()->set_user_defined(GetLogbrokerEndpointWithPort(OriginLogbroker));
    }

    TVector<TRemoteMirrorRule> TRemoteMirrorRuleStorage::GetForTopic(const TString& topicPath) const {
        auto rules = TopicToRules.find(topicPath);
        if (rules == TopicToRules.end()) {
            return {};
        }
        return rules->second;
    }

    std::optional<NLogBroker::SingleModifyRequest> TRemoteMirrorRuleStorage::Add(
        const TRemoteMirrorRule& rule,
        const TString& oAuthToken
    ) {
        if (Exists(rule)) {
            return {};
        }

        KeyToRule[rule.GetKey()] = rule;
        TopicToRules[rule.GetTopicPath()].push_back(rule);

        return rule.GetAddCommand(oAuthToken);
    }

    std::optional<NLogBroker::SingleModifyRequest> TRemoteMirrorRuleStorage::Remove(const TRemoteMirrorRule &rule) {
        if (!Exists(rule)) {
            return {};
        }

        auto storedRule = KeyToRule.find(rule.GetKey());
        if (storedRule != KeyToRule.end()) {
            KeyToRule.erase(storedRule);
        }

        auto rulesIt = TopicToRules.find(rule.GetTopicPath());
        if (rulesIt != TopicToRules.end()) {
            TVector<TRemoteMirrorRule> storedRules = rulesIt->second;
            auto ruleIt = std::find(storedRules.begin(), storedRules.end(), rule);
            if (ruleIt != storedRules.end()) {
                storedRules.erase(ruleIt);
            }
        }
        return rule.GetRemoveCommand();
    }
}
