#include "factory.h"

#include <maps/wikimap/mapspro/libs/acl/include/aclgateway.h>
#include <maps/wikimap/mapspro/libs/acl/include/banrecord.h>
#include <maps/wikimap/mapspro/libs/acl/include/group.h>
#include <maps/wikimap/mapspro/libs/acl/include/permission.h>
#include <maps/wikimap/mapspro/libs/acl/include/permissions.h>
#include <maps/wikimap/mapspro/libs/acl/include/policy.h>
#include <maps/wikimap/mapspro/libs/acl/include/role.h>

#include <yandex/maps/wiki/common/json_helpers.h>
#include <boost/algorithm/string.hpp>

namespace maps::wiki::acl {

User
Factory::user(const pqxx::row& row, Transaction& work)
{
    User::Status status;
    fromString(row["status"].as<std::string>(), status);

    const auto& deleteReasonColumn = row["delete_reason"];
    std::optional<User::DeleteReason> deleteReason = deleteReasonColumn.is_null()
        ? std::nullopt
        : std::optional<User::DeleteReason>(
            enum_io::fromString<User::DeleteReason>(
                deleteReasonColumn.as<std::string>()));

    std::optional<BanRecord> optionalBanRecord;
    if (row["ban_active"].as<bool>(false)) {
        status = User::Status::Banned;
        optionalBanRecord = banRecord(row);
    }

    return User(
        row["id"].as<ID>(),
        row["uid"].as<UID>(),
        row["login"].as<std::string>(),
        row["created"].as<std::string>(),
        row["created_by"].as<UID>(),
        row["modified"].as<std::string>(),
        row["modified_by"].as<UID>(),
        status,
        deleteReason,
        optionalBanRecord,
        row["created_ts"].as<time_t>(),
        row["last_br_ts"].as<time_t>(),
        row["display_name"].as<std::string>(),
        work);
}

BanRecord
Factory::banRecord(const pqxx::row& row)
{
    BanRecord::Action action;
    fromString(row["br_action"].as<std::string>(), action);

    return BanRecord(
        row["br_id"].as<ID>(),
        row["br_uid"].as<UID>(),
        action,
        row["br_created"].as<std::string>(),
        row["br_created_by"].as<UID>(),
        row["br_expires"].as<std::string>({}),
        row["br_reason"].as<std::string>({}));
}

Group
Factory::group(const pqxx::row& row, Transaction& work)
{
    return Factory::group(
        row["id"].as<ID>(),
        row["name"].as<std::string>(),
        row["description"].as<std::string>(),
        work);
}

Group
Factory::group(ID id, const std::string& name, const std::string& description, Transaction& work)
{
    return Group(id, name, description, work);
}

Role
Factory::role(ID id, const std::string& name, const std::string& description, Role::Privacy privacy, Transaction& work)
{
    return Role(id, name, description, privacy, work);
}

Aoi
Factory::aoi(ID id, const std::string& name, const std::string& wkb, Deleted deleted)
{
    return Aoi(id, name, wkb, deleted);
}

Policy
Factory::policy(ID agentId, Role role, ID aoiId, Transaction& work)
{
    return Policy(agentId, std::move(role), aoiId, work);
}

Permission
Factory::permission(ID id, const std::string& name, ID parentId, Transaction& work)
{
    return Permission(id, name, parentId, work);
}

Permissions
Factory::permissions(std::vector<Permission>&& data)
{
    return Permissions(std::move(data));
}

Schedule
Factory::schedule(const pqxx::row& row)
{
    std::optional<std::string> endDate;
    std::optional<std::string> startTime;
    std::optional<std::string> endTime;
    std::optional<int> weekdays;
    std::optional<std::string> workRestDays;

    auto startDate = common::sqlDateToJson(row["date_start"].as<std::string>());
    if (!row["date_end"].is_null()) {
        endDate = common::sqlDateToJson(row["date_end"].as<std::string>());
    }
    if (!row["time_start"].is_null()) {
        startTime = common::sqlTimeToJson(row["time_start"].as<std::string>());
    }
    if (!row["time_end"].is_null()) {
        endTime = common::sqlTimeToJson(row["time_end"].as<std::string>());
    }
    if (!row["weekdays"].is_null()) {
        weekdays = row["weekdays"].as<int>();
    }
    if (!row["work_rest_days_sequence_array"].is_null()) {
        workRestDays = row["work_rest_days_sequence_array"].as<std::string>();
    }

    return Schedule(
        row["id"].as<ID>(),
        startDate, endDate,
        startTime, endTime,
        weekdays, workRestDays);
}

Schedule
Factory::schedule(
    ID id,
    const std::string& startDate,
    const std::optional<std::string>& endDate,
    const std::optional<std::string>& startTime,
    const std::optional<std::string>& endTime,
    const std::optional<int>& weekdays,
    const std::optional<std::string>& workRestDays)
{
    return Schedule(
        id,
        startDate, endDate,
        startTime, endTime,
        weekdays, workRestDays);
}

ScheduledPolicy
Factory::scheduledPolicy(
    ID agentId, ID roleId, ID aoiId,
    const Schedule& schedule)
{
    return ScheduledPolicy(agentId, roleId, aoiId, schedule);
}

ScheduledGroup
Factory::scheduledGroup(
    ID userId, ID groupId,
    const Schedule& schedule)
{
    return ScheduledGroup(userId, groupId, schedule);
}

} // namespace maps::wiki::acl
