#include <maps/wikimap/mapspro/libs/social/magic_strings.h>

#include <yandex/maps/wiki/social/feedback/task_feed.h>
#include <maps/libs/common/include/exception.h>

namespace maps::wiki::social::feedback {

TaskFeedParamsId::TaskFeedParamsId(
    TId beforeTaskId,
    TId afterTaskId,
    uint64_t limit,
    TasksOrder order)
    : ITaskFeedParams(limit)
    , beforeTaskId_(beforeTaskId)
    , afterTaskId_(afterTaskId)
    , order_(order)
{
    REQUIRE(!(beforeTaskId_ && afterTaskId_),
        "'beforeTaskId' and 'afterTaskId' simultaneously not allowed");
}

TaskFeedParamsDateAndId::TaskFeedParamsDateAndId(
    std::optional<DateAndId> before,
    std::optional<DateAndId> after,
    uint64_t limit)
    : ITaskFeedParams(limit)
    , before_(before)
    , after_(after)
{
    REQUIRE(!(before_ && after_),
        "'before' and 'after' simultaneously not allowed");
}

std::string TaskFeedParamsId::sqlFeedOrderByIdClause() const
{
    return
        sql::col::ID +
        " " +
        [&](){
            auto ASC = "ASC", DESC = "DESC";

            switch (order_) {
                case TasksOrder::NewestFirst:
                    return beforeTaskId_ ? ASC : DESC;
                case TasksOrder::OldestFirst:
                    return beforeTaskId_ ? DESC : ASC;
            }
        }();
}

std::string TaskFeedParamsDateAndId::sqlFeedOrderByIdClause() const
{
    auto ASC = "ASC", DESC = "DESC";
    auto orderWord = needReverseAfterSelect() ? ASC : DESC;
    return
        sql::col::STATE_MODIFIED_AT + " " + orderWord + "," +
        sql::col::ID + " " + orderWord;
}

bool TaskFeedParamsId::needReverseAfterSelect() const
{
    return beforeTaskId_ != 0;
}

bool TaskFeedParamsDateAndId::needReverseAfterSelect() const
{
    return before_.has_value();
}

std::string TaskFeedParamsId::sqlFeedWhereClause() const
{
    if (!beforeTaskId_ && !afterTaskId_) {
        return "TRUE";
    }

    auto LESS = " < ", GREATER = " > ";

    const std::string condition = [&](){
        switch (order_) {
            case TasksOrder::NewestFirst:
                return beforeTaskId_ ? GREATER : LESS;
            case TasksOrder::OldestFirst:
                return beforeTaskId_ ? LESS : GREATER;
        }
    }();

    const TId rhsId = beforeTaskId_ ?
        beforeTaskId_ :
        afterTaskId_;

    return sql::col::ID + condition + std::to_string(rhsId);
}

std::string TaskFeedParamsDateAndId::sqlFeedWhereClause() const
{
    if (!before_ && !after_) {
        return "TRUE";
    }

    auto LESS = " < ", GREATER = " > ";
    const std::string condition = before_ ? GREATER : LESS;

    const auto dateAndId = before_ ? before_.value() : after_.value();

    return "(" + sql::col::STATE_MODIFIED_AT + "," + sql::col::ID + ")"
        + condition
        + "("
            + "'" + chrono::formatSqlDateTime(dateAndId.date()) + "',"
            + std::to_string(dateAndId.id())
        + ")";
}

TaskFeed
constructTasksFeed(Tasks&& tasks, const ITaskFeedParams& feedParams)
{
    auto hasMore = HasMore::No;
    if (tasks.size() > feedParams.limit()) {
        hasMore = HasMore::Yes;
        tasks.pop_back();
    }

    if (feedParams.needReverseAfterSelect()) {
        std::reverse(tasks.begin(), tasks.end());
    }

    return TaskFeed(std::move(tasks), hasMore);
}

TaskFeed::TaskFeed(Tasks tasks, HasMore hasMore)
    : tasks(std::move(tasks))
    , hasMore(hasMore)
{}

TaskFeedWithCount::TaskFeedWithCount(
    Tasks tasks, HasMore hasMore, uint64_t totalCount)
        : TaskFeed(tasks, hasMore)
        , totalCount(totalCount)
{}

} // namespace maps::wiki::social::feedback
