#include <maps/wikimap/mapspro/libs/taskutils/include/task.h>

#include <maps/wikimap/mapspro/libs/taskutils/impl/reader.h>
#include <maps/wikimap/mapspro/libs/taskutils/impl/tasks_impl.h>
#include <maps/wikimap/mapspro/libs/taskutils/impl/taskmanager_impl.h>

namespace maps::wiki::taskutils {

Task::Task(std::shared_ptr<TaskImpl> impl)
    : impl_(impl)
{}

Task::~Task()
{}

TaskID
Task::id() const
{
    return impl_->token().id();
}

const TaskInfo&
Task::info() const
{
    return impl_->info();
}

const Token&
Task::token() const
{
    return impl_->token();
}

TaskResult
Task::load(Transaction& work) const
{
    Reader reader(impl_->commonData().dbSchema());
    return reader.load(work, impl_->token());
}

TaskResult
Task::start(Transaction& work) const
{
    TransactionAutoCleaner<Transaction> guard(work);

    const auto& token = impl_->token();
    if (token.expired()) {
        return TaskManagerImpl::createTaskResult(TaskStatus::Expired);
    }

    const auto& schema = impl_->commonData().dbSchema();
    auto query =
        "UPDATE " + schema + ".taskutils_tasks SET status='started'"
        " WHERE status='created' AND id=" + std::to_string(token.id());

    auto r = work.exec(query);
    if (!r.affected_rows()) {
        return TaskResult();
    }

    guard.commit();

    return TaskManagerImpl::createTaskResult(TaskStatus::Started);
}

TaskResult
Task::finish(Transaction& work, const std::string& result) const
{
    TransactionAutoCleaner<Transaction> guard(work);

    const auto& token = impl_->token();
    if (token.expired()) {
        return TaskManagerImpl::createTaskResult(TaskStatus::Expired);
    }

    const auto& schema = impl_->commonData().dbSchema();
    auto query =
        "UPDATE " + schema + ".taskutils_tasks SET status='done', "
            "finished=NOW(), result=" + work.quote(result) +
        " WHERE status='started' AND id=" + std::to_string(token.id());

    auto r = work.exec(query);
    if (!r.affected_rows()) {
        return TaskResult();
    }

    guard.commit();

    return TaskManagerImpl::createTaskResult(TaskStatus::Done, result);
}

TaskResult
Task::fail(Transaction& work, const std::string& errorMessage) const
{
    TransactionAutoCleaner<Transaction> guard(work);

    const auto& token = impl_->token();
    const auto& schema = impl_->commonData().dbSchema();
    auto query =
        "UPDATE " + schema + ".taskutils_tasks SET status='failed', "
            "finished=NOW(), error_message=" + work.quote(errorMessage) +
        " WHERE status IN ('created','started') AND id=" + std::to_string(token.id());

    auto r = work.exec(query);
    if (!r.affected_rows()) {
        return TaskResult();
    }

    guard.commit();

    return TaskManagerImpl::createTaskResult(TaskStatus::Failed, errorMessage);
}

} // namespace maps::wiki::taskutils
