#include "common.h"

#include "config.h"
#include "exception.h"
#include "magic_strings.h"
#include "moderation_status.h"
#include "user_info.h"

#include <maps/wikimap/mapspro/libs/acl/include/check_context.h>
#include <maps/wikimap/mapspro/libs/acl/include/exception.h>
#include <maps/wikimap/mapspro/libs/acl/include/subject_path.h>
#include <yandex/maps/wiki/common/json_helpers.h>

namespace maps::wiki::aclsrv {

namespace {

const acl::SubjectPath ACL_BASE_PATH("mpro/acl");
const acl::SubjectPath PERMISSIONS_ACL_PATH = ACL_BASE_PATH("permissions");
const acl::SubjectPath ASSIGN_ACL_PATH = ACL_BASE_PATH("assign");
const acl::SubjectPath BANS_ACL_PATH = ACL_BASE_PATH("ban");
const std::string IDM_SERVICE = "acl";
const std::string IDM_ADMIN_ROLE = "admin";

} // namespace

std::string outputPermissionId(const std::string& dbPermissionId)
{
    return dbPermissionId == "empty_value_id" ? "" : dbPermissionId;
}

std::string dbPermissionId(const std::string& outputPermissionId)
{
    return "" == outputPermissionId ? "empty_value_id" : outputPermissionId;
}

std::vector<ScheduleData>
parseSchedules(const json::Value& schedulesJson)
{
    ASSERT(schedulesJson.isArray());

    std::vector<ScheduleData> result;
    for (const auto& val : schedulesJson) {
        result.push_back(ScheduleData());
        auto& scheduleData = result.back();
        scheduleData.startDate = common::readField<std::string>(val, DATE_START);
        if (val.hasField(DATE_END)) {
            scheduleData.endDate = common::readField<std::string>(val, DATE_END);
        }
        if (val.hasField(TIME_START)) {
            scheduleData.startTime = common::readField<std::string>(val, TIME_START);
        }
        if (val.hasField(TIME_END)) {
            scheduleData.endTime = common::readField<std::string>(val, TIME_END);
        }
        if (val.hasField(WEEKDAYS)) {
            scheduleData.weekdays = common::readField<int>(val, WEEKDAYS);
        }
        if (val.hasField(SEQUENCE)) {
            scheduleData.workRestDays = (json::Builder() << val[SEQUENCE]).str();
        }
    }
    return result;
}

bool
equal(
    const ScheduleData& scheduleData,
    const acl::Schedule& schedule)
{
    return scheduleData.startDate == schedule.startDate() &&
           scheduleData.endDate == schedule.endDate() &&
           scheduleData.startTime == schedule.startTime() &&
           scheduleData.endTime == schedule.endTime() &&
           scheduleData.weekdays == schedule.weekdays() &&
           ((!scheduleData.workRestDays && !schedule.workRestDays())
           ||
           acl::parseWorkRestDays(*scheduleData.workRestDays) ==
            schedule.workRestDays());
}

bool
equal(
    const acl::ScheduledPolicy& scheduledPolicy,
    const acl::Policy& policy)
{
    return
        scheduledPolicy.roleId() == policy.roleId() &&
        scheduledPolicy.aoiId() == policy.aoiId();
}

bool
equal(
    const PolicyData& data,
    const acl::Policy& policy)
{
    return
        !data.schedule &&
        data.roleId == policy.roleId() &&
        data.aoiId == policy.aoiId();
}

bool
equal(
    const PolicyData& data,
    const acl::ScheduledPolicy& scheduledPolicy)
{
    return
        data.schedule &&
        data.roleId == scheduledPolicy.roleId() &&
        data.aoiId == scheduledPolicy.aoiId() &&
        equal(*data.schedule, scheduledPolicy.schedule());
}

bool
equal(
    const GroupData& data,
    const acl::Group& group)
{
    return
        !data.schedule &&
        data.id == group.id();
}

bool
equal(
    const GroupData& data,
    const acl::ScheduledGroup& scheduledGroup)
{
    return
        data.schedule &&
        data.id == scheduledGroup.groupId() &&
        equal(*data.schedule, scheduledGroup.schedule());
}

std::vector<acl::ScheduledPolicy>::const_iterator
find(
    const std::vector<acl::ScheduledPolicy>& scheduledPolicies,
    const acl::Policy& policy)
{
    return std::find_if(
        scheduledPolicies.begin(),
        scheduledPolicies.end(),
        [&](const auto& scheduledPolicy) {
            return equal(scheduledPolicy, policy);
        });
}

CombinedScheduledObjects
combineScheduledObjects(const acl::ScheduledObjects& scheduledObjects)
{
    CombinedScheduledObjects result;
    for (const auto& scheduledPolicy : scheduledObjects.policies) {
        const auto policyKey = std::make_pair(
            scheduledPolicy.roleId(), scheduledPolicy.aoiId());
        result.policies[policyKey].push_back(scheduledPolicy.schedule());
    }
    for (const auto& scheduledGroup : scheduledObjects.groups) {
        result.groups[scheduledGroup.groupId()].push_back(scheduledGroup.schedule());
    }
    return result;
}

} // namespace maps::wiki::aclsrv
