#pragma once

#include <maps/libs/geolib/include/bounding_box.h>
#include <yandex/maps/wiki/common/string_utils.h>
#include <yandex/maps/mrc/toloka_client/assignment.h>

#include <iosfwd>
#include <string>
#include <unordered_map>
#include <vector>

namespace maps {

namespace geolib3 {

std::ostream& operator<<(std::ostream& out, const BoundingBox& box);

} // geolib3

namespace mrc {
namespace toloka {

using Rectangle = geolib3::BoundingBox;
using Rectangles = std::vector<Rectangle>;

// Input parameters of a single task of sign detection
struct TaskInput
{
    std::string source;
    TaskInput() = default;
    TaskInput(std::string source_)
        : source(std::move(source_))
    {}
};

// Output parameters of a single task of sign detection
struct TaskOutput
{
    Rectangles rectangles;
    TaskOutput() = default;
    TaskOutput(Rectangles rectangles_)
        : rectangles(std::move(rectangles_))
    {}
};

// Result of a sign detection task
struct TaskResult
{
    TaskInput input;
    TaskOutput output;
};

using TaskResults = std::vector<TaskResult>;


struct TaskSuite
{
    std::string id;
    std::string poolId;
    size_t overlap;
    std::vector<TaskInput> tasks;
};

using TaskSuites = std::vector<TaskSuite>;
using IdToTaskSuite = std::unordered_map<std::string, TaskSuite>;

// Raw result of a task suite from a single user
struct AssignmentResult
{
    std::string taskSuiteId;
    std::string assignmentId;
    io::AssignmentStatus status;
    std::string userId;
    std::vector<TaskInput> inputs;
    std::vector<TaskOutput> outputs;
};

using AssignmentResults = std::vector<AssignmentResult>;

// Map from task suite ID to collection of results from multiple users
using TaskSuiteIdToResults
        = std::unordered_map<std::string, AssignmentResults>;


/**
 * User's statistic for task suite
 * @param id_
 * @param assignmentId_
 */
struct UserStat
{
    UserStat(std::string uid_, std::string assignmentId_, io::AssignmentStatus status_)
        : uid(std::move(uid_))
        , assignmentId(std::move(assignmentId_))
        , assignmentStatus(status_)
    {}

    std::string uid;
    std::string assignmentId;
    io::AssignmentStatus assignmentStatus;
    size_t submittedSignsCount = 0;
    size_t truePositives = 0;
};

using UserIdToStat = std::unordered_map<std::string, UserStat>;

// Result of processing task suite:
// @param taskResults - merged results for each task in the suite
// @param userIdToStat - per-user statistic
struct TaskSuiteResult
{
    TaskResults taskResults;
    UserIdToStat userIdToStat;
    size_t detectedSignsCount = 0;
};

template <typename StreamType, typename T>
StreamType& operator<<(StreamType& out, const std::vector<T>& vec)
{
    return out << "[ " << wiki::common::join(vec, ", ") << " ]";
}

std::ostream& operator<<(std::ostream& out, const TaskInput& taskInput);
std::ostream& operator<<(std::ostream& out, const TaskOutput& taskOutput);
std::ostream& operator<<(std::ostream& out, const TaskResult& taskResult);
std::ostream& operator<<(std::ostream& out, const TaskSuite& taskSuite);
std::ostream& operator<<(std::ostream& out, const AssignmentResult& result);
std::ostream& operator<<(std::ostream& out, const TaskSuiteIdToResults& idToResults);
std::ostream& operator<<(std::ostream& out, const UserStat& userStat);

} // toloka
} // mrc
} // maps
