/*

 * Copyright (c) 2025 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 "appspawn_utils.h"

#include "cJSON.h"

#include "config_policy_utils.h"

#include "dec_api.h"

#include "dec_config.h"

#include "hilog/log.h"

#include "json_utils.h"



static std::vector<cJSON *> InitDecConfig(void)

{

    std::vector<cJSON *> sandboxConfig = {};

    CfgFiles *files = GetCfgFiles("etc/sandbox");

    for (int i = 0; (files != nullptr) && (i < MAX_CFG_POLICY_DIRS_CNT); ++i) {

        if (files->paths[i] == nullptr) {

            continue;

        }

        std::string path = files->paths[i];

        std::string appPath = path + "/appdata-sandbox.json";

        APPSPAWN_LOGV("Init sandbox dec config %{public}s", appPath.c_str());

        cJSON *config = GetJsonObjFromFile(appPath.c_str());

        APPSPAWN_CHECK((config != nullptr && cJSON_IsObject(config)), continue,

            "Failed to init sandbox dec config %{public}s", appPath.c_str());

        sandboxConfig.push_back(config);

    }

    FreeCfgFiles(files);

    return sandboxConfig;

}



static void DestroyDecConfig(std::vector<cJSON *> &sandboxConfig)

{

    for (auto& config : sandboxConfig) {

        APPSPAWN_CHECK_ONLY_EXPER(config != nullptr, continue);

        cJSON_Delete(config);

        config = nullptr;

    }

    sandboxConfig.clear();

}



static std::string ConvertDecPath(std::string path)

{

    std::vector<std::pair<std::string, std::string>> replacements = {

        {"<currentUserId>", "currentUser"},

        {"<PackageName>", "com.ohos.dlpmanager"}

    };



    for (const auto& [from, to] : replacements) {

        size_t pos = 0;

        while ((pos = path.find(from, pos)) != std::string::npos) {

            path.replace(pos, from.length(), to);

            pos += to.length();

        }

    }

    return path;

}



static void AddDecPathsByPermission(std::map<std::string, std::vector<std::string>> &decMap,

                                    const std::string permission, const cJSON *permItem)

{

    cJSON *mountPoints = cJSON_GetObjectItemCaseSensitive(permItem, "mount-paths");

    APPSPAWN_CHECK(mountPoints != nullptr && cJSON_IsArray(mountPoints), return,

        "Don't get mountPoints json from permission");



    std::vector<std::string> decPaths = {};

    int arraySize = cJSON_GetArraySize(mountPoints);

    for (int i = 0; i < arraySize; ++i) {

        cJSON *mntPoint = cJSON_GetArrayItem(mountPoints, i);

        APPSPAWN_CHECK(mntPoint != nullptr && cJSON_IsObject(mntPoint), continue,

            "Don't get mntPoint json from mountPoints");



        cJSON *decPathJson = cJSON_GetObjectItemCaseSensitive(mntPoint, "dec-paths");

        if (decPathJson == nullptr || !cJSON_IsArray(decPathJson)) {

            APPSPAWN_LOGV("Don't get decPath json from mntPoint");

            continue;

        }



        int count = cJSON_GetArraySize(decPathJson);

        for (int j = 0; j < count; ++j) {

            cJSON *item = cJSON_GetArrayItem(decPathJson, j);

            APPSPAWN_CHECK(item != nullptr && cJSON_IsString(item), continue, "Don't get decPath item");

            const char *strValue = cJSON_GetStringValue(item);

            APPSPAWN_CHECK_ONLY_EXPER(strValue != nullptr, continue);



            std::string decPath = ConvertDecPath(strValue);

            APPSPAWN_LOGV("Get decPath %{public}s from %{public}s", decPath.c_str(), permission.c_str());

            decPaths.push_back(decPath);

        }

    }



    if (!decPaths.empty()) {

        auto it = decMap.find(permission);

        if (it == decMap.end()) {

            decMap[permission] = decPaths;

        } else {

            for (const auto& path : decPaths) {

                it->second.push_back(path);

            }

        }

    }

}



static void ProcessConfig(const cJSON *config, std::map<std::string, std::vector<std::string>> &decMap)

{

    cJSON *permission = cJSON_GetObjectItemCaseSensitive(config, "permission");

    APPSPAWN_CHECK(permission != nullptr && cJSON_IsArray(permission), return,

        "Don't get permission json from config");



    int arraySize = cJSON_GetArraySize(permission);

    for (int i = 0; i < arraySize; ++i) {

        cJSON *item = cJSON_GetArrayItem(permission, i);

        APPSPAWN_CHECK(item != nullptr && cJSON_IsObject(item), continue, "Invalid item in permission array");

        cJSON *permissionChild = item->child;

        while (permissionChild != nullptr && cJSON_IsArray(permissionChild)) {

            cJSON *permItem = cJSON_GetArrayItem(permissionChild, 0);

            APPSPAWN_CHECK(permItem != nullptr && cJSON_IsObject(permItem), permissionChild = permissionChild->next;

                continue, "Don't get permission item");

            APPSPAWN_LOGV("AddDecPathsByPermission %{public}s", permissionChild->string);

            AddDecPathsByPermission(decMap, permissionChild->string, permItem);

            permissionChild = permissionChild->next;

        }

    }

}



std::map<std::string, std::vector<std::string>> GetDecPathMap(void)

{

    std::vector<cJSON *> sandboxConfig = InitDecConfig();

    APPSPAWN_CHECK(!sandboxConfig.empty(), return {}, "Sandbox dec config is empty");



    std::map<std::string, std::vector<std::string>> decMap = {};

    for (auto& config : sandboxConfig) {

        ProcessConfig(config, decMap);

    }



    DestroyDecConfig(sandboxConfig);

    return decMap;

}



std::vector<std::pair<std::string, int>> GetIgnoreCaseDirs(void)

{

    std::vector<std::pair<std::string, int>> result;

    if (!IsNoShareFsEnable()) {

        return result;

    }

    static const DecIgnoreCaseInfo list[] = { DEC_IGNORE_CASE_LIST };

    size_t count = sizeof(list) / sizeof(list[0]);

    result.reserve(count);

    for (size_t i = 0; i < count; i++) {

        result.emplace_back(list[i].path, list[i].mode);

    }

    return result;

}