#include "config.h"
#include "server.h"

#include <library/cpp/resource/resource.h>
#include <util/stream/file.h>

namespace NBalancerServer {
    void AddLuaObject(IOutputStream& config, TStringBuf name, const TVector<std::pair<TStringBuf, TStringBuf>>& options) {
        config << name << " = {\n";
        for (const auto& [optionName, optionValue] : options) {
            config << "    " << optionName << " = " << optionValue << ";\n";
        }
        config << "}; --" << name << Endl;
    };

    void AddLuaObject(IOutputStream& config, TStringBuf name, TStringBuf content) {
        if (content.StartsWith("return")) {
            config << TString(content).replace(0, 6, TString(name) + " = ") << ";" << Endl;
        } else {
            config << name << " = {\n";
            config << content;
            config << "}; --" << name << Endl;
        }
    };

    namespace {
        size_t NextUID = 0;
    }

    const TString instanceConfigTemplate = NResource::Find("/instance_config");
    const TString rootConfigTemplate = NResource::Find("/root_config");

    TBalancerInstanceConfig::TBalancerInstanceConfig(const TMainTaskOptions& options) {
        TStringOutput instanceConfig(LuaInstanceConfig);

        instanceConfig << instanceConfigTemplate;

        Globals["workers"] = Max(options.NetworkThreads, 1ul);
        Globals["maxconn"] = options.MaxConnPerNetworkThread;
        Globals["updater_required"] = options.UpdaterRequired;
        if (options.LogPath) {
            Globals["log"] = options.LogPath;
        }
        if (options.DynamicBalancingLogPath) {
            Globals["dynamic_balancing_log"] = options.DynamicBalancingLogPath;
        }
        Globals["admin_port"] = options.AdminPort;

        MainOptions.WorkerStartDelay = options.WorkerStartDelay;
        MainOptions.AllowEmptyAdminAddrs = true;
        MainOptions.ContExecutionTimeLogThreshold = options.ContExecutionTimeLogThreshold;
    }

    TBalancerRootConfig::TBalancerRootConfig(const TServerOptions& options)
        : Id_(++NextUID)
    {
        if (options.SslPort) {
            if (!options.SslConfigPath && !options.SslConfig) {
                ythrow yexception() << "empty ssl configh";
            }

            if (options.SslConfig && options.SslConfigPath) {
                ythrow yexception() << "both ssl_config and ssl_config_path are set";
            }

            if (options.Port == options.SslPort) {
                ythrow yexception() << "same port for http and https requests";
            }
        }

        TStringOutput rootConfig(LuaRootConfig);

        if (options.AccessLog) {
            AddLuaObject(rootConfig, "accesslog", {
                {"log", options.AccessLog.Quote()}
            });
        }

        TString sslConfig;

        if (options.SslConfig) {
            sslConfig = options.SslConfig;
        } else if (options.SslConfigPath) {
            TFileInput fi(options.SslConfigPath);
            sslConfig = fi.ReadAll();
        }

        AddLuaObject(rootConfig, "ssl_sni", sslConfig);

        rootConfig << rootConfigTemplate;

        Globals["port"] = options.Port;
        Globals["ssl_port"] = options.SslPort;
        Globals["maxlen"] = options.MaxLen;
        Globals["maxreq"] = options.MaxReq;
        Globals["shared_uuid"] = "common_modules_" + ToString(Id_);
    }
}
