/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "admin_manager.h"

#include <algorithm>
#include <ctime>
#include <fstream>
#include <iostream>

#include "directory_ex.h"
#include "edm_constants.h"
#include "edm_log.h"

namespace OHOS {
namespace EDM {
std::shared_ptr<AdminManager> AdminManager::instance_;
std::mutex AdminManager::mutexLock_;

std::shared_ptr<AdminManager> AdminManager::GetInstance()
{
    if (instance_ == nullptr) {
        std::lock_guard<std::mutex> autoLock(mutexLock_);
        if (instance_ == nullptr) {
            instance_.reset(new (std::nothrow) AdminManager());
        }
    }
    return instance_;
}

AdminManager::AdminManager()
{
    EDMLOGI("AdminManager::AdminManager");
}

AdminManager::~AdminManager()
{
    EDMLOGI("AdminManager::~AdminManager");
    admins_.clear();
}

bool AdminManager::GetAdminByUserId(int32_t userId, std::vector<std::shared_ptr<Admin>> &userAdmin)
{
    userAdmin.clear();
    auto iter = admins_.find(userId);
    if (iter == admins_.end()) {
        EDMLOGW("GetAdminByUserId::get userId Admin failed. userId = %{public}d", userId);
        return false;
    }
    userAdmin = iter->second;
    return true;
}

void AdminManager::GetAdminBySubscribeEvent(ManagedEvent event,
    std::unordered_map<int32_t, std::vector<std::shared_ptr<Admin>>> &subscribeAdmins)
{
    for (const auto &adminItem : admins_) {
        std::vector<std::shared_ptr<Admin>> subAdmin;
        for (const auto &it : adminItem.second) {
            std::vector<ManagedEvent> events = it->adminInfo_.managedEvents_;
            if (std::find(events.begin(), events.end(), event) != events.end()) {
                subAdmin.push_back(it);
            }
        }
        if (!subAdmin.empty()) {
            subscribeAdmins[adminItem.first] = subAdmin;
        }
    }
}

ErrCode AdminManager::SetAdminValue(int32_t userId, const Admin &adminItem)
{
    std::shared_ptr<Admin> getAdmin = GetAdminByPkgName(adminItem.adminInfo_.packageName_, userId);
    if (getAdmin != nullptr) {
        return UpdateAdmin(getAdmin, userId, adminItem);
    }
    if (!AdminPoliciesStorageRdb::GetInstance()->InsertAdmin(userId, adminItem)) {
        EDMLOGE("AdminManager::SetAdminValue insert failed.");
        return EdmReturnErrCode::SYSTEM_ABNORMALLY;
    }
    std::vector<std::shared_ptr<Admin>> admins;
    GetAdminByUserId(userId, admins);
    std::shared_ptr<Admin> admin = std::make_shared<Admin>(adminItem);
    admins.emplace_back(admin);
    admins_[userId] = admins;
    return ERR_OK;
}

std::shared_ptr<Admin> AdminManager::GetAdminByPkgName(const std::string &packageName, int32_t userId)
{
    std::shared_ptr<Admin> subOrSuperAdmin;
    if (userId != EdmConstants::DEFAULT_USER_ID &&
        SUCCEEDED(GetSubOrSuperAdminByPkgName(packageName, subOrSuperAdmin))) {
        EDMLOGD("GetAdminByPkgName::get sub-super or super admin: %{public}s", packageName.c_str());
        return subOrSuperAdmin;
    }
    std::vector<std::shared_ptr<Admin>> userAdmin;
    if (!GetAdminByUserId(userId, userAdmin)) {
        EDMLOGW("GetAdminByPkgName::get userId Admin failed. userId = %{public}d", userId);
        return nullptr;
    }
    for (const auto &item : userAdmin) {
        if (item->adminInfo_.packageName_ == packageName) {
            return item;
        }
    }
    EDMLOGD("GetAdminByPkgName::get admin failed. admin size = %{public}u, packageName = %{public}s",
        (uint32_t)userAdmin.size(), packageName.c_str());
    return nullptr;
}

ErrCode AdminManager::DeleteAdmin(const std::string &packageName, int32_t userId)
{
    auto iterMap = admins_.find(userId);
    if (iterMap == admins_.end()) {
        EDMLOGW("DeleteAdmin::get userId Admin failed. userId = %{public}d", userId);
        return ERR_EDM_UNKNOWN_ADMIN;
    }
    auto adminPoliciesStorageRdb = AdminPoliciesStorageRdb::GetInstance();
    if (adminPoliciesStorageRdb == nullptr) {
        EDMLOGE("AdminManager::DeleteAdmin get adminPoliciesStorageRdb failed.");
        return ERR_GET_STORAGE_RDB_FAILED;
    }
    if (!adminPoliciesStorageRdb->DeleteAdmin(userId, packageName)) {
        EDMLOGW("delete admin (%{public}s) failed!", packageName.c_str());
        return ERR_EDM_DEL_ADMIN_FAILED;
    }
    auto iter = std::remove_if(iterMap->second.begin(), iterMap->second.end(),
        [&](std::shared_ptr<Admin> admin) { return admin->adminInfo_.packageName_ == packageName; });
    iterMap->second.erase(iter, iterMap->second.end());
    if (iterMap->second.empty()) {
        admins_.erase(iterMap);
    }
    return ERR_OK;
}

ErrCode AdminManager::UpdateAdmin(std::shared_ptr<Admin> getAdmin, int32_t userId, const Admin &adminItem)
{
    if (getAdmin == nullptr) {
        EDMLOGW("UpdateAdmin::get null admin, never get here");
        return EdmReturnErrCode::SYSTEM_ABNORMALLY;
    }
    if (getAdmin->GetAdminType() != AdminType::NORMAL && getAdmin->GetAdminType() != adminItem.GetAdminType()) {
        EDMLOGE("AdminManager::UpdateAdmin sub-super or delegated admin can not update to another type.");
        return EdmReturnErrCode::ADMIN_EDM_PERMISSION_DENIED;
    }
    if (getAdmin->GetAdminType() == AdminType::NORMAL && adminItem.GetAdminType() != AdminType::NORMAL &&
        adminItem.GetAdminType() != AdminType::ENT) {
        EDMLOGE("AdminManager::UpdateAdmin normal admin can not update to sub-super admin or delegated admin.");
        return EdmReturnErrCode::ADMIN_EDM_PERMISSION_DENIED;
    }
    if (!AdminPoliciesStorageRdb::GetInstance()->UpdateAdmin(userId, adminItem)) {
        EDMLOGW("UpdateAdmin::update admin failed.");
        return EdmReturnErrCode::SYSTEM_ABNORMALLY;
    }
    getAdmin->adminInfo_.adminType_ = adminItem.adminInfo_.adminType_;
    getAdmin->adminInfo_.entInfo_ = adminItem.adminInfo_.entInfo_;
    getAdmin->adminInfo_.permission_ = adminItem.adminInfo_.permission_;
    getAdmin->adminInfo_.accessiblePolicies_ = adminItem.adminInfo_.accessiblePolicies_;
    return ERR_OK;
}

// success is returned as long as there is a super administrator
bool AdminManager::IsSuperAdminExist()
{
    std::vector<std::shared_ptr<Admin>> userAdmin;
    bool ret = GetAdminByUserId(EdmConstants::DEFAULT_USER_ID, userAdmin);
    if (!ret) {
        EDMLOGD("IsSuperAdminExist::not find super Admin");
        return false;
    }
    return std::any_of(userAdmin.begin(), userAdmin.end(),
        [](const std::shared_ptr<Admin> &admin) { return admin->adminInfo_.adminType_ == AdminType::ENT; });
}

bool AdminManager::IsSuperAdmin(const std::string &bundleName)
{
    std::shared_ptr<Admin> admin = GetAdminByPkgName(bundleName, EdmConstants::DEFAULT_USER_ID);
    if (admin == nullptr) {
        EDMLOGW("IsSuperAdmin: admin == nullptr.");
        return false;
    }
    if (admin->adminInfo_.adminType_ == AdminType::ENT) {
        EDMLOGW("IsSuperAdmin: admin->adminInfo_.adminType_ == AdminType::ENT.");
        return true;
    }
    return false;
}

bool AdminManager::IsAdminExist()
{
    return !admins_.empty();
}

bool AdminManager::IsSuperOrSubSuperAdmin(const std::string &bundleName)
{
    std::shared_ptr<Admin> superAdmin;
    superAdmin = GetAdminByPkgName(bundleName, EdmConstants::DEFAULT_USER_ID);
    if (superAdmin == nullptr) {
        return false;
    }
    return superAdmin->adminInfo_.adminType_ == AdminType::ENT ||
        superAdmin->adminInfo_.adminType_ == AdminType::SUB_SUPER_ADMIN;
}

/*
 * There are different administrator types according to the input parameters.
 * Returns a list of package names
 */
void AdminManager::GetEnabledAdmin(AdminType role, std::vector<std::string> &packageNameList, int32_t userId)
{
    packageNameList.clear();
    std::vector<std::shared_ptr<Admin>> userAdmin;
    bool ret = GetAdminByUserId(userId, userAdmin);
    if (!ret) {
        EDMLOGW("GetEnabledAdmin::not find enabled Admin. userId = %{public}d", userId);
        return;
    }
    EDMLOGD("AdminManager:GetEnabledAdmin adminType: %{public}d , admin size: %{public}zu", role, userAdmin.size());
    if (static_cast<int32_t>(role) >= static_cast<int32_t>(AdminType::UNKNOWN) ||
        static_cast<int32_t>(role) < static_cast<int32_t>(AdminType::NORMAL)) {
        EDMLOGD("there is no admin(%{public}u) device manager package name list!", role);
        return;
    }

    for (const auto &item : userAdmin) {
        if (item->adminInfo_.adminType_ == role) {
            std::string adminName = item->adminInfo_.packageName_ + "/" + item->adminInfo_.className_;
            packageNameList.push_back(adminName);
        }
    }
}

ErrCode AdminManager::GetSubOrSuperAdminByPkgName(const std::string &subAdminName,
    std::shared_ptr<Admin> &subOrSuperAdmin)
{
    std::vector<std::shared_ptr<Admin>> userAdmin;
    if (!GetAdminByUserId(EdmConstants::DEFAULT_USER_ID, userAdmin)) {
        EDMLOGW("GetSubOrSuperAdminByPkgName::not find Admin under default user id");
        return ERR_EDM_SUPER_ADMIN_NOT_FOUND;
    }
    auto adminItem = std::find_if(userAdmin.begin(), userAdmin.end(), [&](const std::shared_ptr<Admin> &admin) {
        return admin->adminInfo_.packageName_ == subAdminName && (admin->GetAdminType() == AdminType::ENT ||
            admin->GetAdminType() == AdminType::SUB_SUPER_ADMIN || admin->GetAdminType() == AdminType::VIRTUAL_ADMIN);
    });
    if (adminItem == userAdmin.end()) {
        EDMLOGW("GetSubOrSuperAdminByPkgName::not find sub-super admin or super Admin");
        return ERR_EDM_SUPER_ADMIN_NOT_FOUND;
    }
    subOrSuperAdmin = *adminItem;
    return ERR_OK;
}

ErrCode AdminManager::GetSubSuperAdminsByParentName(const std::string &parentName, std::vector<std::string> &subAdmins)
{
    if (subAdmins.size() > 0) {
        subAdmins.clear();
    }
    std::vector<std::shared_ptr<Admin>> userAdmin;
    if (!GetAdminByUserId(EdmConstants::DEFAULT_USER_ID, userAdmin)) {
        EDMLOGE("GetSubSuperAdminsByParentName::not find Admin under default user id.");
        return ERR_EDM_SUPER_ADMIN_NOT_FOUND;
    }
    for (const auto &admin : userAdmin) {
        if ((admin->GetAdminType() == AdminType::SUB_SUPER_ADMIN ||
            admin->GetAdminType() == AdminType::VIRTUAL_ADMIN) &&
            admin->GetParentAdminName() == parentName) {
            subAdmins.push_back(admin->adminInfo_.packageName_);
        }
    }
    return ERR_OK;
}

ErrCode AdminManager::GetEntInfo(const std::string &packageName, EntInfo &entInfo, int32_t userId)
{
    std::vector<std::shared_ptr<Admin>> userAdmin;
    bool ret = GetAdminByUserId(userId, userAdmin);
    if (!ret) {
        EDMLOGW("GetEntInfo::not find Admin. userId = %{public}d", userId);
        return ERR_EDM_UNKNOWN_ADMIN;
    }
    for (const auto &item : userAdmin) {
        if (item->adminInfo_.packageName_ == packageName) {
            entInfo = item->adminInfo_.entInfo_;
            return ERR_OK;
        }
    }
    return ERR_EDM_UNKNOWN_ADMIN;
}

ErrCode AdminManager::SetEntInfo(const std::string &packageName, EntInfo &entInfo, int32_t userId)
{
    std::vector<std::shared_ptr<Admin>> userAdmin;
    bool ret = GetAdminByUserId(userId, userAdmin);
    if (!ret) {
        EDMLOGW("SetEntInfo::not find Admin. userId = %{public}d", userId);
        return ERR_EDM_UNKNOWN_ADMIN;
    }
    auto adminPoliciesStorageRdb = AdminPoliciesStorageRdb::GetInstance();
    if (adminPoliciesStorageRdb == nullptr) {
        EDMLOGE("AdminManager::SetEntInfo get adminPoliciesStorageRdb failed.");
        return ERR_GET_STORAGE_RDB_FAILED;
    }
    for (auto &item : userAdmin) {
        if (item->adminInfo_.packageName_ == packageName &&
            adminPoliciesStorageRdb->UpdateEntInfo(userId, packageName, entInfo)) {
            item->adminInfo_.entInfo_ = entInfo;
            return ERR_OK;
        }
    }
    return ERR_EDM_UNKNOWN_ADMIN;
}

ErrCode AdminManager::SaveSubscribeEvents(const std::vector<uint32_t> &events, const std::string &bundleName,
    int32_t userId)
{
    std::shared_ptr<Admin> admin = GetAdminByPkgName(bundleName, userId);
    if (admin == nullptr) {
        return ERR_EDM_UNKNOWN_ADMIN;
    }
    auto adminPoliciesStorageRdb = AdminPoliciesStorageRdb::GetInstance();
    if (adminPoliciesStorageRdb == nullptr) {
        EDMLOGE("AdminManager::SaveSubscribeEvents get adminPoliciesStorageRdb failed.");
        return ERR_GET_STORAGE_RDB_FAILED;
    }
    std::vector<ManagedEvent> oldManagedEvents = admin->adminInfo_.managedEvents_;
    size_t eventsNumber = admin->adminInfo_.managedEvents_.size();
    for (const auto &event : events) {
        std::vector<ManagedEvent> managedEvents = admin->adminInfo_.managedEvents_;
        ManagedEvent subEvent = static_cast<ManagedEvent>(event);
        if (std::find(managedEvents.begin(), managedEvents.end(), subEvent) == managedEvents.end()) {
            admin->adminInfo_.managedEvents_.push_back(subEvent);
        }
    }
    if (admin->adminInfo_.managedEvents_.size() > eventsNumber &&
        !adminPoliciesStorageRdb->UpdateManagedEvents(userId, admin->adminInfo_.packageName_,
            admin->adminInfo_.managedEvents_)) {
        admin->adminInfo_.managedEvents_ = oldManagedEvents;
        return ERR_EDM_UNKNOWN_ADMIN;
    }
    return ERR_OK;
}

ErrCode AdminManager::RemoveSubscribeEvents(const std::vector<uint32_t> &events, const std::string &bundleName,
    int32_t userId)
{
    std::shared_ptr<Admin> admin = GetAdminByPkgName(bundleName, userId);
    if (admin == nullptr) {
        return ERR_EDM_UNKNOWN_ADMIN;
    }
    auto adminPoliciesStorageRdb = AdminPoliciesStorageRdb::GetInstance();
    if (adminPoliciesStorageRdb == nullptr) {
        EDMLOGE("AdminManager::RemoveSubscribeEvents get adminPoliciesStorageRdb failed.");
        return ERR_GET_STORAGE_RDB_FAILED;
    }

    std::vector<ManagedEvent> oldManagedEvents = admin->adminInfo_.managedEvents_;
    size_t eventsNumber = admin->adminInfo_.managedEvents_.size();
    auto iter = std::remove_if(admin->adminInfo_.managedEvents_.begin(), admin->adminInfo_.managedEvents_.end(),
        [&](ManagedEvent managedEvent) {
            return std::find(events.begin(), events.end(), static_cast<uint32_t>(managedEvent)) != events.end();
        });
    admin->adminInfo_.managedEvents_.erase(iter, admin->adminInfo_.managedEvents_.end());

    if (admin->adminInfo_.managedEvents_.size() < eventsNumber &&
        !adminPoliciesStorageRdb->UpdateManagedEvents(userId, admin->adminInfo_.packageName_,
            admin->adminInfo_.managedEvents_)) {
        admin->adminInfo_.managedEvents_ = oldManagedEvents;
        return ERR_EDM_UNKNOWN_ADMIN;
    }
    return ERR_OK;
}

ErrCode AdminManager::GetPoliciesByVirtualAdmin(const std::string &bundleName, const std::string &parentName,
    std::vector<std::string> &policies)
{
    policies.clear();
    std::shared_ptr<Admin> virtualAdmin = GetAdminByPkgName(bundleName, EdmConstants::DEFAULT_USER_ID);
    if (virtualAdmin == nullptr) {
        return EdmReturnErrCode::PARAM_ERROR;
    }
    if (virtualAdmin->GetAdminType() != AdminType::VIRTUAL_ADMIN || virtualAdmin->GetParentAdminName() != parentName) {
        EDMLOGE("GetPoliciesByVirtualAdmin the administrator does not have permission to get delegated policies.");
        return EdmReturnErrCode::ADMIN_EDM_PERMISSION_DENIED;
    }
    policies = virtualAdmin->adminInfo_.accessiblePolicies_;
    return ERR_OK;
}

void AdminManager::GetVirtualAdminsByPolicy(const std::string &policyName, const std::string &parentName,
    std::vector<std::string> &bundleNames)
{
    bundleNames.clear();
    std::vector<std::shared_ptr<Admin>> admins;
    GetAdminByUserId(EdmConstants::DEFAULT_USER_ID, admins);
    for (auto adminItem : admins) {
        if (adminItem->GetAdminType() != AdminType::VIRTUAL_ADMIN || adminItem->GetParentAdminName() != parentName) {
            continue;
        }
        auto policies = adminItem->adminInfo_.accessiblePolicies_;
        if (std::find(policies.begin(), policies.end(), policyName) != policies.end()) {
            bundleNames.emplace_back(adminItem->adminInfo_.packageName_);
        }
    }
}

bool AdminManager::HasPermissionToHandlePolicy(std::shared_ptr<Admin> admin, const std::string &policyName)
{
    if (admin->GetAdminType() != AdminType::VIRTUAL_ADMIN) {
        return true;
    }
    auto policies = admin->adminInfo_.accessiblePolicies_;
    return std::find(policies.begin(), policies.end(), policyName) != policies.end();
}

std::shared_ptr<Admin> AdminManager::GetSuperAdmin()
{
    if (admins_.find(EdmConstants::DEFAULT_USER_ID) != admins_.end()) {
        auto item = std::find_if(admins_[EdmConstants::DEFAULT_USER_ID].begin(),
            admins_[EdmConstants::DEFAULT_USER_ID].end(),
            [&](const std::shared_ptr<Admin>& admin) { return admin->GetAdminType() == AdminType::ENT; });
        if (item != admins_[EdmConstants::DEFAULT_USER_ID].end()) {
            return *item;
        }
    }
    return nullptr;
}

// init
void AdminManager::Init()
{
    auto adminPoliciesStorageRdb = AdminPoliciesStorageRdb::GetInstance();
    if (adminPoliciesStorageRdb != nullptr) {
        admins_ = adminPoliciesStorageRdb->QueryAllAdmin();
    } else {
        EDMLOGE("AdminManager::Init failed.");
    }
}

void AdminManager::Dump()
{
    for (const auto &entry : admins_) {
        EDMLOGI("AdminManager::Dump %{public}d.", entry.first);
        for (const auto &admin : entry.second) {
            EDMLOGI("AdminManager::Dump admin info adminType_ %{public}d.",
                admin->adminInfo_.adminType_);
            EDMLOGI("AdminManager::Dump admin info packageName_ %{public}s.",
                admin->adminInfo_.packageName_.c_str());
            EDMLOGI("AdminManager::Dump admin info parentAdminName_ %{public}s.",
                admin->adminInfo_.parentAdminName_.c_str());
        }
    }
}
} // namespace EDM
} // namespace OHOS