#include "config_tab_worker.h"
#include <library/cpp/yconf/patcher/unstrict_config.h>
#include <library/cpp/logger/global/global.h>
#include <util/folder/path.h>

namespace NRTYDeploy {
    using namespace NDMInterface;
    class TConfigTabWorkerParser : public TConfigTabWorker {
    public:
        TConfigTabWorkerParser(const char* filenamePrefix, const char* rootSection, TConfigField::TType attributeType, TConfigField::TType zoneType)
            : FilenamePrefix(filenamePrefix)
            , RootSection(rootSection)
            , AttributeType(attributeType)
            , ZoneType(zoneType)
        {}

        void FillTabImpl(TConfigTab& tab, IVersionedStorage& storage, const TProjectConfig::TComponent* component, const TProjectConfig& /*projectConfig*/) const override {
            tab.SetRequired(false);
            AddField(tab, RootSection + ".Attributes.*", "", TConfigField::HTML_PARSER_ATTRIBUTE);
            AddField(tab, RootSection + ".Zones.*", "", TConfigField::HTML_PARSER_ZONE);
            tab.SetEnabled(false);
            tab.SetFilePath("/configs/" + component->GetComponent().GetServiceName() + "/" + FilenamePrefix);
            for (const auto& file : component->GetFiles().GetFile()) {
                if (TFsPath(file.GetPath()).GetName().StartsWith(FilenamePrefix)) {
                    TString data;
                    if (!storage.GetValue(file.GetPath(), data, file.GetVersion()))
                        ythrow yexception() << "cannot get data from " << file.GetPath() << ", version " << file.GetVersion();
                    TUnstrictConfig cfg;
                    if (cfg.ParseMemory(data.data())) {
                        tab.SetEnabled(true);
                        tab.SetFilePath(file.GetPath());
                        TVector<TString> path = SplitString(RootSection, ".");
                        TYandexConfig::Section* s = cfg.GetRootSection();
                        if (!s)
                            return;
                        for (const auto& name : path) {
                            TYandexConfig::TSectionsMap sections = s->GetAllChildren();
                            TYandexConfig::TSectionsMap::iterator i = sections.find(name);
                            if (i == sections.end())
                                return;
                            s = i->second;
                        }
                        TYandexConfig::TSectionsMap sections = s->GetAllChildren();
                        TYandexConfig::TSectionsMap::const_iterator i = sections.find("Attributes");
                        if (i != sections.end())
                        for (const auto& dir : i->second->GetDirectives())
                            AddField(tab, RootSection + ".Attributes." + dir.first, dir.second, AttributeType);
                        i = sections.find("Zones");
                        if (i != sections.end())
                        for (const auto& dir : i->second->GetDirectives())
                            AddField(tab, RootSection + ".Zones." + dir.first, dir.second, ZoneType);
                    }
                }
            }
        }

        void SetChangedConfigImpl(const TConfigTab& tab, IVersionedStorage& storage) override {
            TUnstrictConfig cfg;
            VERIFY_WITH_LOG(cfg.ParseMemory(""), "cannot parse empty config");
            for (const auto& field : tab.GetFields())
                cfg.PatchEntry(field.GetName(), field.GetValue());
            if (!storage.SetValue(tab.GetFilePath(), cfg.ToString()))
                ythrow yexception() << "cannot write " << tab.GetFilePath();
        }
    protected:
        TString FilenamePrefix;
        TString RootSection;
        TConfigField::TType AttributeType;
        TConfigField::TType ZoneType;
    private:
        void AddField(TConfigTab& tab, const TString& name, const TString& value, TConfigField::TType type) const {
            TConfigField& field = *tab.AddFields();
            SetFieldName(field, name);
            field.SetDefaultState(TConfigField::NO_DEFAULT);
            field.SetType(type);
            SetFieldValue(field, value);
        }
    };

    class TConfigTabWorkerHTML : public TConfigTabWorkerParser {
    public:
        TConfigTabWorkerHTML()
            : TConfigTabWorkerParser("html.conf", "HtmlParser", TConfigField::HTML_PARSER_ATTRIBUTE, TConfigField::HTML_PARSER_ZONE)
        {}

        void FillTabImpl(TConfigTab& tab, IVersionedStorage& storage, const TProjectConfig::TComponent* component, const TProjectConfig& projectConfig) const override {
            tab.SetType(TConfigTab::RTYSERVER_HTML_PARSER);
            tab.SetTabName("HTML Parser");
            TConfigTabWorkerParser::FillTabImpl(tab, storage, component, projectConfig);
        }
        static TFactory::TRegistrator<TConfigTabWorkerHTML> Registrator;

    };

    TConfigTabWorkerHTML::TFactory::TRegistrator<TConfigTabWorkerHTML> TConfigTabWorkerHTML::Registrator(TConfigTab::RTYSERVER_HTML_PARSER);

    class TConfigTabWorkerXML : public TConfigTabWorkerParser {
    public:
        TConfigTabWorkerXML()
            : TConfigTabWorkerParser("xml.conf", "XMLParser.DOCTYPE", TConfigField::XML_PARSER_ATTRIBUTE, TConfigField::XML_PARSER_ZONE) {
        }

        void FillTabImpl(TConfigTab& tab, IVersionedStorage& storage, const TProjectConfig::TComponent* component, const TProjectConfig& projectConfig) const override {
            tab.SetType(TConfigTab::RTYSERVER_XML_PARSER);
            tab.SetTabName("XML Parser");
            TConfigTabWorkerParser::FillTabImpl(tab, storage, component, projectConfig);
        }
        static TFactory::TRegistrator<TConfigTabWorkerXML> Registrator;

    };

    TConfigTabWorkerXML::TFactory::TRegistrator<TConfigTabWorkerXML> TConfigTabWorkerXML::Registrator(TConfigTab::RTYSERVER_XML_PARSER);
}
