#pragma once

#include <library/cpp/digest/lower_case/hash_ops.h>

#include <util/generic/string.h>

namespace NSrvKernel {

class TStringStorage {
public:
    TStringStorage() noexcept
    {}

    explicit TStringStorage(TStringBuf data) noexcept
        : Data_(data)
    {}

    explicit TStringStorage(const char *data) noexcept
        : Data_(data)
    {}

    explicit TStringStorage(const TString& data) noexcept
        : Holder_(data)
        , Data_(Holder_ ?: TStringBuf())
    {}

    TStringStorage(const TStringStorage& rhs) noexcept
    {
        Holder_ = rhs.Holder_;
        Data_ = Holder_ ?: rhs.Data_;
    }

    TStringStorage& operator=(const TStringStorage& rhs) noexcept {
        TStringStorage temp(rhs);
        Swap(temp);
        return *this;
    }

    TStringStorage(TStringStorage&& rhs) noexcept
    {
        Holder_ = std::move(rhs.Holder_);
        Data_ = Holder_ ?: rhs.Data_;
    }

    TStringStorage& operator=(TStringStorage&& rhs) noexcept {
        TStringStorage temp(std::move(rhs));
        Swap(temp);
        return *this;
    }

    void MakeOwned() noexcept {
        if (Data_ && !Holder_) {
            Holder_ = Data_;
            Data_ = Holder_;
        }
    }

    TStringBuf AsStringBuf() const noexcept {
        return Data_;
    }

    TString AsString() const noexcept {
        return TString{Data_};
    }

    size_t size() const noexcept {
        return Data_.Size();
    }

    void Clear() noexcept {
        Data_.Clear();
        Holder_.clear();
    }

    bool Empty() const noexcept {
        return Data_.Empty();
    }

    operator bool() const noexcept {
        return !Data_.Empty();
    }

    void Swap(TStringStorage& stringStorage) noexcept {
        std::swap(Data_, stringStorage.Data_);
        std::swap(Holder_, stringStorage.Holder_);
        if (Holder_) {
            Data_ = Holder_;
        }
        if (stringStorage.Holder_) {
            stringStorage.Data_ = stringStorage.Holder_;
        }
    }

    bool operator<(const TStringStorage& rhs) const noexcept {
        return AsStringBuf() < rhs.AsStringBuf();
    }

    friend bool operator==(TStringBuf lhs, const TStringStorage& rhs) noexcept {
        return lhs == rhs.AsStringBuf();
    }

    friend bool operator==(const TStringStorage& lhs, TStringBuf rhs) noexcept {
        return lhs.AsStringBuf() == rhs;
    }

public:
    class TCIOps : public ::TCIOps {
    public:
        using ::TCIOps::operator();

        size_t operator()(const TStringStorage& lhs) const noexcept {
            return ::TCIOps::operator()(lhs.AsStringBuf());
        }

        size_t operator()(const TStringStorage& lhs, const TStringStorage& rhs) const noexcept {
            return ::TCIOps::operator()(lhs.AsStringBuf(), rhs.AsStringBuf());
        }

        size_t operator()(const TStringStorage& lhs, TStringBuf rhs) const noexcept {
            return ::TCIOps::operator()(lhs.AsStringBuf(), rhs);
        }
    };

private:
    TString Holder_;
    TStringBuf Data_;
};
}  // namespace NSrvKernel
