#pragma once

#include "platform.h"

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/toloka/task_type_info.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/txn_id.h>

#include <maps/libs/sql_chemistry/include/gateway_access.h>
#include <maps/libs/enum_io/include/enum_io_fwd.h>

#include <optional>
#include <string>
#include <vector>

namespace maps::mrc::db::toloka {

enum class TaskStatus {
    New,
    InProgress,
    Finished,
    Failed,
    Free,
    Cancelling,
    Cancelled,
    Min = New,
    Max = Cancelled
};

DECLARE_ENUM_IO(TaskStatus);

/// Represents items in toloka_mgr.task table which stores data
/// for tasks in Toloka Manager system.
class Task {
public:
    using OptionalString = std::optional<std::string>;

    explicit Task(Platform platform)
        : platform_(platform)
    {}

    TId id() const { return id_; }

    TId txnId() const { return txnId_; }

    Platform platform() const { return platform_; }

    TaskType type() const { return static_cast<TaskType>(type_); }

    TaskStatus status() const { return status_; }

    const std::string& inputValues() const { return inputValues_; }

    const OptionalString& outputValues() const { return outputValues_; }

    int overlap() const { return overlap_; }

    chrono::TimePoint createdAt() const { return createdAt_; }

    OptionalTimePoint postedAt() const { return postedAt_; }

    OptionalTimePoint solvedAt() const { return solvedAt_; }

    const OptionalString& knownSolutions() const { return knownSolutions_; }

    const OptionalString& messageOnUnknownSolution() const { return messageOnUnknownSolution_; }

    Task& setType(TaskType type) {
        type_ = static_cast<int>(type);
        return *this;
    }

    Task& setStatus(TaskStatus status) {
        status_ = status;
        return *this;
    }

    Task& setInputValues(std::string values) {
        inputValues_ = std::move(values);
        return *this;
    }

    Task& setOutputValues(std::string values) {
        outputValues_ = std::move(values);
        return *this;
    }

    Task& setOverlap(int overlap) {
        overlap_ = overlap;
        return *this;
    }

    Task& setCreatedAt(chrono::TimePoint timePoint) {
        createdAt_ = timePoint;
        return *this;
    }

    Task& setPostedAt(chrono::TimePoint timePoint) {
        postedAt_ = timePoint;
        return *this;
    }

    Task& setSolvedAt(chrono::TimePoint timePoint) {
        solvedAt_ = timePoint;
        return *this;
    }

    Task& setKnownSolutions(std::string solutions) {
        knownSolutions_ = std::move(solutions);
        return *this;
    }

    Task& setMessageOnUnknownSolution(std::string solutions) {
        messageOnUnknownSolution_ = std::move(solutions);
        return *this;
    }

private:
    friend class sql_chemistry::GatewayAccess<Task>;
    friend class TxnIdAccess<Task>;

    Task& setTxnId(TId txnId) {
        txnId_ = txnId;
        return * this;
    }

    Task() = default;

    template <typename T>
    static auto introspect(T& t) {
        return std::tie(t.id_, t.txnId_, t.platform_, t.type_, t.status_, t.inputValues_, t.outputValues_, t.overlap_,
            t.createdAt_, t.postedAt_, t.solvedAt_, t.knownSolutions_, t.messageOnUnknownSolution_);
    }

    TId id_{};
    TId txnId_{};
    Platform platform_;
    int type_{};
    TaskStatus status_{TaskStatus::New};
    std::string inputValues_{};
    OptionalString outputValues_{};
    int overlap_{};
    chrono::TimePoint createdAt_{};
    OptionalTimePoint postedAt_{};
    OptionalTimePoint solvedAt_{};
    OptionalString knownSolutions_{};
    OptionalString messageOnUnknownSolution_{};

public:
    auto introspect() const { return introspect(*this); }
};

using Tasks = std::vector<Task>;

} // namespace maps::mrc::db::toloka
