#pragma once

#include "common.h"

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

#include <maps/wikimap/mapspro/services/mrc/libs/toloka_manager/include/approvement.h>

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

namespace maps {
namespace mrc {
namespace toloka {

struct Approvement {

using TaskAnswer = ApprovementAnswer;

// Input parameters of a single task
using TaskInput = ApprovementInput;

// Output parameters of a single task
using TaskOutput = ApprovementOutput;

// Result of a 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 a single task suite
 */
struct UserStat {
    UserStat(const AssignmentResult& result)
        : userId(result.userId)
        , assignmentId(result.assignmentId)
        , assignmentStatus(result.status)
        , tasksCount(result.inputs.size())
        , correctCount(0)
    {
    }

    std::string userId;
    std::string assignmentId;
    io::AssignmentStatus assignmentStatus;
    size_t tasksCount;   // Total number of tasks in the task suite
    double correctCount; // Number of tasks correctly answered by the user
};

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;
};

// Map from task suite ID to merged result
using TaskSuiteIdToResultMap
    = std::unordered_map<std::string, TaskSuiteResult>;

/**
 * Merge results for a single task received from multiple users
 * If there is no answer chosen by the majority of users,
 * the resulting answer is set to NotLoaded
 */
static TaskOutput mergeSingleTaskResults(
    const AssignmentResults& results,
    size_t taskIdx,
    UserIdToStat& userIdToStat);

/**
 * Merge single task suite results received from multiple users
 */
static TaskSuiteResult mergeTaskSuiteResults(
    const AssignmentResults& assignmentResults);

/**
 * Evaluate and accept/reject user assignments
 */
static void evaluateAssignments(
    const io::TolokaClient& tolokaClient,
    const TaskSuiteResult& tsResult);

}; // struct Approvement

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