#include "module.h"

#include <balancer/kernel/coro/coro_async.h>
#include <balancer/kernel/http/parser/http.h>
#include <balancer/kernel/log/errorlog.h>
#include <balancer/kernel/module/module.h>
#include <balancer/kernel/regexp/regexp_re2.h>
#include <balancer/kernel/requester/requester.h>
#include <balancer/kernel/http/parser/response_builder.h>

#include <kernel/querydata/idl/querydata_structs.pb.h>
#include <search/idl/meta.pb.h>

using namespace NConfig;
namespace NSrvKernel {

MODULE(thumbs_spreader) {
    TModule(const TModuleParams& mp)
        : TModuleBase(mp)
    {
        Config->ForEach(this);

        if (!Regexp_) {
            ythrow TConfigParseError() << "no regexp configured";
        }

        if (!Module_) {
            ythrow TConfigParseError() << "no module configured";
        }
    }

private:
    bool CheckExists(TString id, IWorkerCtl& process) const {
        TAsyncRequester requester{*Module_, nullptr, process};

        TRequest request;
        {
            TString requestString;
            requestString += "GET /i?id=";
            requestString += id;
            requestString += "&exists=1";
            requestString += " HTTP/1.1\r\n\r\n";
            Y_UNUSED(request.Parse(std::move(requestString)));
        }
        TResponse response;
        TChunkList contentList;
        // We consider all errors as false
        if (TError err = requester.Requester().Request(std::move(request), response, contentList)) {
            return false;
        }
        // exists returns 200 if image exists
        return response.ResponseLine().StatusCode == 200;
    }

    START_PARSE {
        TString idRegExp;
        ON_KEY ("id_regexp", idRegExp) {
            THolder<TRegexp>(new TRegexp(idRegExp)).Swap(Regexp_);
            return;
        }

        if (key == "module") {
            TSubLoader(Copy(value->AsSubConfig())).Swap(Module_);
            return;
        }

    } END_PARSE

    TError DoRun(const TConnDescr& descr) const noexcept override {
        LOG_ERROR(TLOG_ERR, descr, "thumbs spreader started");

        TVector<TStringBuf> capturedGroups;
        Regexp_->Extract(descr.Request->RequestLine().CGI.AsStringBuf(), &capturedGroups, true);

        if (capturedGroups.size() <= 1) {
            descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "no match");
            return {};
        }

        auto idsChunk = capturedGroups[1];
        TVector<TString> ids;
        TVector<TCoroFuture<bool>> exists;
        for (TStringBuf id : StringSplitter(idsChunk).Split(',')) {
            exists.push_back(CoroAsync("exist_check", &descr.Process().Executor(), [id, this, &descr]() {
                return CheckExists(TString(id), descr.Process());
            }));
        }

        TString result = "";
        for (auto& res : exists) {
            result.push_back(res.Get() ? '1' : '0');
        }
        TResponse response = BuildResponse().Version11()
                                            .Code(HTTP_OK)
                                            .ContentLength(result.size());
        Y_TRY(TError, error) {
            Y_PROPAGATE_ERROR(descr.Output->SendHead(std::move(response), false, TInstant::Max()));
            Y_PROPAGATE_ERROR(descr.Output->Send(TChunkList(result), TInstant::Max()));
            return descr.Output->SendEof(TInstant::Max());
        } Y_CATCH {
            descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "client write error");
            return error;
        };

        descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "success");
        return {};
    }

    bool DoExtraAccessLog() const noexcept override {
        return true;
    }

private:
    THolder<IModule> Module_;
    THolder<TRegexp> Regexp_;
};

}  // namespace NSrvKernel

NSrvKernel::IModuleHandle* NModThumbsExist::Handle() {
    return TModule::Handle();
}
