#pragma once

#include "method.h"
#include "protocol_version.h"
#include "resolved_endpoint.h"

#include <balancer/kernel/memory/chunks.h>

#include <library/cpp/http/io/headers.h>
#include <library/cpp/cgiparam/cgiparam.h>

#include <util/generic/maybe.h>
#include <util/generic/string.h>
#include <util/generic/hash.h>
#include <util/datetime/base.h>

#include <typeindex>
#include <any>

namespace NHttp {
/// Класс TRequest содержит параметры запроса
class TRequest {
  public:
    /// Адрес хоста, на который нужно послать запрос.
    /// Если не указан, то его должна определить реализация Requester
    TMaybe<TResolvedEndpoint> Endpoint;

    /// Версия протокола
    /// Для HTTP/2 необходим заголовок Host
    EProtocolVersion ProtocolVersion = EProtocolVersion::HTTP11;

    /// Включает TLS
    /// SSL SNI берётся из заголовка Host
    /// Для отладки можно добавить переменную окружения SSLKEYLOGFILE
    /// SSLKEYLOGFILE указывает на файл, куда логировать ключи от защищенной сессии
    /// Этот файл далее можно указать в Wireshark, чтобы получать
    /// расшифрованный траффик в реальном времени. Подробнее: https://wiki.wireshark.org/TLS#tls-decryption
    bool Secure = false;

    /// HTTP метод
    EMethod Method = EMethod::GET;

    /// Часть url после домена
    TString Path = "/";

    /// query параметры. Пример: name=value&name2=value2
    TCgiParameters Query;

    /// Заголовки
    THttpHeaders Headers;

    /// Тело запроса
    NSrvKernel::TChunkList Body;

    /// Заголовки после тела запроса
    THttpHeaders TrailerHeaders;

    /// Если включено, то тело запроса посылается с помощью Chunked-Encoding.
    /// Первый чанк - то, что лежит в поле Body. Последующие нужно посылать через IRequestContext.
    bool ChunkedRequest = false;

    /// Метод GetMagicOptions позволяет передать любые данные в любой из модулей,
    /// через которые проходит данный запрос. Не рекомендуется использовать,
    /// но практика показывает, что без этого порой необойтись. Красивые общие
    /// интерфейсы и костыли для реализации хитроумной бизнес логики не совместимы.
    /// Этот метод был заложен еще при проектировании.
    /// © @vlad-kolotvin
    template<typename T> typename T::TRequestOptions& GetMagicOptions(T* module = nullptr);
  private:
    THashMap<std::pair<std::type_index, void*>, std::any> MagicOptions_;
};

template<typename T> typename T::TRequestOptions& TRequest::GetMagicOptions(T* module) {
    return *std::any_cast<typename T::TRequestOptions>(&MagicOptions_[std::make_pair(std::type_index(typeid(T)), module)]);
}
}
