#include "ddk_fields.h"
#include "ddk_globals.h"
#include "ddk_parsed_entity.h"

#include <saas/rtyserver/factors/factor.h>
#include <saas/rtyserver/factors/factors_abstract.h>
#include <saas/library/sharding/rules/urlhash.h>

#include <util/digest/fnv.h>

namespace NRTYServer {

namespace NDDK {
    static_assert(sizeof(ui32) == sizeof(float), "typesize mismatch in DDK");

    constexpr size_t DefaultFieldSize = sizeof(ui32) * 8;
    constexpr size_t SmallFieldSize = sizeof(ui16) * 8;

    class TFieldsDescription: public IRTYStaticFactors {
    private:
        TVector<NRTYFactors::TSimpleFactorDescription> Factors;

    public:
        TFieldsDescription() {
            Factors.push_back(NRTYFactors::TSimpleFactorDescription("Livetime", DeadlineIndex, DefaultFieldSize, TBitsReader::ftInt));
            Factors.push_back(NRTYFactors::TSimpleFactorDescription("Version", VersionIndex, DefaultFieldSize, TBitsReader::ftInt));
            Factors.push_back(NRTYFactors::TSimpleFactorDescription("StartLiveTime", TimestampIndex, DefaultFieldSize, TBitsReader::ftInt));
            for (ui32 i = 0; i < HashFields; ++i) {
                Factors.push_back(NRTYFactors::TSimpleFactorDescription("Identifier" + ToString(i), HashIndex + i, DefaultFieldSize, TBitsReader::ftInt));
            }
            Factors.push_back(NRTYFactors::TSimpleFactorDescription("NewVersionSource", SourceWithNewVersionIndex, DefaultFieldSize, TBitsReader::ftInt));
            Factors.push_back(NRTYFactors::TSimpleFactorDescription("ParsedEntitiesHashIndex", ParsedEntitiesHashIndex, DefaultFieldSize, TBitsReader::ftInt));
            Factors.push_back(NRTYFactors::TSimpleFactorDescription("StreamId", StreamIdIndex, SmallFieldSize, TBitsReader::ftInt));
        }

        void WriteIdentifier(TBasicFactorStorage& ddkBlock, const TDocSearchInfo& docInfo) const {
            const TDocSearchInfo::THash hash = docInfo.GetHash();
            (TDocSearchInfo::THash&)ddkBlock[HashIndex] = hash;
        }

    public: // IRTYStaticFactors
        bool LoadErfBlock(TBasicFactorStorage& data, const TParsedDocument& document) const override {
            const auto* docDDK = document.GetComponentEntity<TRTYDDKParsedEntity>(DDK_COMPONENT_NAME);
            VERIFY_WITH_LOG(docDDK, "there is no ddk in doc %s", document.GetDocSearchInfo().GetUrl().data());

            (ui32&)data[DeadlineIndex] = docDDK->GetDeadlineMinutesUTC();
            (ui32&)data[VersionIndex] = docDDK->GetVersion();
            (ui32&)data[TimestampIndex] = docDDK->GetTimestampUTC();
            (ui32&)data[ParsedEntitiesHashIndex] = document.GetEntitiesHash();

            if (document.GetDocSearchInfo().HasIndexName()) {
                ui32 hash = FnvHash<i32>(document.GetDocSearchInfo().GetIndexName().data(), document.GetDocSearchInfo().GetIndexName().size());
                (ui32&)data[SourceWithNewVersionIndex] = hash;
            }

            data[StreamIdIndex] = docDDK->GetStreamId();
            WriteIdentifier(data, document.GetDocSearchInfo());

            return true;
        }

        const NRTYFactors::TSimpleFactorDescription& GetFactor(ui32 index) const override {
            Y_ASSERT(index < Factors.size());
            return Factors[index];
        }

        ui32 GetStaticFactorsCount() const override {
            return KeysCount;
        }
    };
}
}

const IRTYStaticFactors& GetDDKFields() {
    return *Singleton<NRTYServer::NDDK::TFieldsDescription>();
}

void SetHash(TBasicFactorStorage& ddkBlock, const TDocSearchInfo& info) {
    VERIFY_WITH_LOG(ddkBlock.Size() >= NRTYServer::NDDK::KeysCount, "ddkBlock argument is too small");
    Y_VERIFY_DEBUG(ddkBlock.Size() < NRTYServer::NDDK::KeysCount * 3, "ddkBlock argument is suspiciously big");

    Singleton<NRTYServer::NDDK::TFieldsDescription>()->WriteIdentifier(ddkBlock, info);
}
