#include "attributes.h"
#include "validate.h"

#include <kernel/indexer/faceproc/docattrs.h>
#include <kernel/indexer/posindex/faceint.h>

namespace {
    template <class T>
    bool CheckValueType(const TString& str) {
        T value;
        return TryFromString<T>(str, value);
    }
}


namespace NRTYServer {

const size_t TSearchAttrLimits::MAX_NAME_LENGTH = MAXATTRNAME_BUF - 1;
const size_t TSearchAttrLimits::MAX_TOTAL_LENGTH = MAXKEY_LEN - 3;

void ValidateSearchAttribute(const NRTYServer::TAttribute& attr) {
    const TString& attrValue = attr.GetValue();
    if (attrValue == "__delete__") {
        return;
    }

    const TString& attrNameRaw = attr.GetName();
    TString attrName = attrNameRaw;
    attrName.to_lower();

    if (attrName.size() > TSearchAttrLimits::MAX_NAME_LENGTH) {
        throw yexception() << attrNameRaw << " search attribute name length exceeds the limit of " << TSearchAttrLimits::MAX_NAME_LENGTH;
    }
    if (attrName.size() + attrValue.size() > TSearchAttrLimits::MAX_TOTAL_LENGTH) {
        throw yexception() << attrNameRaw << " search attribute total length exceeds the limit of " << TSearchAttrLimits::MAX_TOTAL_LENGTH;
    }
    if (attrValue.empty()) {
        throw yexception() << attrNameRaw << " search attribute has empty value";
    }
    if (attrName.empty()) {
        throw yexception() << "empty search attribute name with value " << attrValue;
    }
    for (auto s = attrName.data(); s < attrName.end(); ++s) {
        if (!(*s >= 'a' && *s <= 'z') && *s != '_' && !(*s >= '0' && *s <= '9')) {
            throw yexception() << attrNameRaw << " search attribute name contains disallowed symbol " << *s;
        }
    }

    if (attr.type() == NRTYServer::TAttribute::INTEGER_ATTRIBUTE) {
        TString resultValue = TAttrs::ExtractValue(attrValue, TFullDocAttrs::AttrSearchInteger);
        if (!CheckValueType<ui32>(resultValue))
            throw yexception() << "incorrect search attribute value type for " << attrNameRaw;
    } else if (attr.type() != NRTYServer::TAttribute::LITERAL_ATTRIBUTE) {
        ythrow yexception() << "incorrect search attribute type for " << attrNameRaw;
    }
}

void ValidateGroupAttribute(const NRTYServer::TAttribute& attr) {
    const TString& value = attr.GetValue();
    if (value == "__delete__") {
        return;
    }

    const TString& name = attr.GetName();
    const TFullDocAttrs::EAttrType type = attr.GetType() == NRTYServer::TAttribute::INTEGER_ATTRIBUTE
        ? TFullDocAttrs::AttrGrInt
        : TFullDocAttrs::AttrGrName;

    if (!name) {
        throw yexception() << "incorrect grouping attribute with empty name, value " << value;
    }
    if (!value) {
        throw yexception() << "incorrect grouping attribute " << name << " with empty value";
    }

    TString resultValue = TAttrs::ExtractValue(value, type);
    if (type == TFullDocAttrs::AttrGrInt) {
        if (!CheckValueType<TCateg>(resultValue))
            throw yexception() << "incorrect grouping attribute value type for " << name;
        TCateg category = FromString<TCateg>(resultValue);
        if (category < 0 || category == Max<TCateg>())
            throw yexception() << "incorrect grouping attribute value type for " << name << ": " << category << ". Must be in range [0, " << Max<TCateg>() << ")" << Endl;
    } else if (type == TFullDocAttrs::AttrGrName) {
        if (value.find('\n') != TString::npos)
            throw yexception() << "grouping attr " << name << " value has \\n symbol: " << value.Quote();
    }
}

}
