#include <library/cpp/uri/http_url.h>
#include <library/cpp/string_utils/url/url.h>
#include <library/cpp/protobuf/json/proto2json.h>
#include <robot/gemini/protos/base.pb.h>

#include "gem_canon.h"

namespace NWebmaster {

void TResolvedUrl::SetErrorCode(int errorCode) {
    HasError = true;
    ErrorCode = errorCode;

    switch (ErrorCode) {
        case NGeminiProtos::BAD_URL:
            ErrorName = "BAD_URL"; break;
        case NGeminiProtos::URL_NOT_FOUND:
            ErrorName = "URL_NOT_FOUND"; break;
        case NGeminiProtos::QUOTA_EXCEEDED:
            ErrorName = "QUOTA_EXCEEDED"; break;
        case NGeminiProtos::TIMEOUT_EXCEEDED:
            ErrorName = "TIMEOUT_EXCEEDED"; break;
        case NGeminiProtos::UNKNOWN_ERROR:
            ErrorName = "UNKNOWN_ERROR"; break;
        default:;
    }
}

void TResolvedUrl::SetCanonizedUrl(const TString &canonized, bool keepPath) {
    Canonized = canonized;

    if (Canonized.find("://") == TString::npos) {
        Canonized = "http://" + Canonized;
    }

    THttpURL url;
    THttpURL::TParsedState parsedState = url.Parse(Canonized, THttpURL::FeaturesRecommended);

    if (THttpURL::ParsedOK != parsedState) {
        SetErrorCode(NGeminiProtos::BAD_URL);
        return;
    }

    if (!keepPath) {
        size_t hostLen = url.PrintS(THttpURL::FlagHostPort | THttpURL::FlagScheme).size();
        Canonized = Canonized.substr(0, hostLen);
    }
}

void TReceiver::Push(const NGeminiProtos::TCastorResponse& response) {
    TResolvedUrl record;

    record.Original = response.GetOriginalUrl();

    if (response.HasError()) {
        record.SetErrorCode(response.GetError());
    }

    if (response.HasCanonizationType()) {
        switch(response.GetCanonizationType()) {
        case NGeminiProtos::CASE_HOST:
            if (response.HasHost()) {
                record.SetCanonizedUrl(response.GetHost(), false);
            }
            break;

        case NGeminiProtos::MIRROR:
            if (response.HasCanonizedUrl()) {
                record.SetCanonizedUrl(response.GetCanonizedUrl(), false);
            }
            break;

        case NGeminiProtos::WEAK:
            if (response.MainUrlSize() > 0) {
                record.SetCanonizedUrl(response.GetMainUrl(0));
            }
            break;

        default:
            ;
        }
    }

    if (record.Canonized.empty()) {
        record.SetCanonizedUrl(record.Original);
        if (!response.HasError()) {
            record.SetErrorCode(NGeminiProtos::URL_NOT_FOUND);
        }
    }

    Records.push_back(record);
}

TGeminiCanonizer::TGeminiCanonizer(const TString &user, size_t rps, size_t timeout, NGeminiProtos::ECanonizationType type)
    : Type(type)
{
    Config.Reset(new NGeminiClient::TConfig());

    Config->User = user;
    Config->MaxRps = rps;
    Config->CanonizationType = type;
    Config->RunningTimeLimit = TDuration::Seconds(timeout);

    Client.Reset(new NGeminiClient::TClient(Config, &Receiver));
}

bool TGeminiCanonizer::GetCanonical(const TDeque<TString> &urls, TDeque<TResolvedUrl> &resolved) {
    NGeminiClient::TRequestQueue requestQueue;
    TVector<NGeminiClient::TRequestData> data;

    for (auto url: urls) {
        NGeminiClient::TRequestData d;
        if (url.find("://") == TString::npos) {
            url = "http://" + url;
        }
        d.SetUrl(url);
        data.push_back(d);
    }

    NGeminiClient::TDataSourcePtr ds(new NGeminiClient::TContainerDataSource(data));
    requestQueue.Push(ds);
    Receiver.Records.clear();
    Client->Run(requestQueue);
    resolved.swap(Receiver.Records);
    return true;
}

} //namespace wmc
