/*
 * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). All Rights Reserved.
 *
 * This software is the property of WSO2 LLC. and its suppliers, if any.
 * Dissemination of any information or reproduction of any material contained
 * herein in any form is strictly forbidden, unless permitted by WSO2 expressly.
 * You may not alter or remove any copyright or other notice from copies of this content.
 */

package org.wso2.carbon.is.migration.service.v700.service;

import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
import org.wso2.carbon.identity.application.common.model.ApplicationPermission;
import org.wso2.carbon.identity.application.common.model.Scope;
import org.wso2.carbon.identity.application.mgt.provider.RegistryBasedApplicationPermissionProvider;
import org.wso2.carbon.identity.core.migrate.MigrationClientException;
import org.wso2.carbon.is.migration.service.v700.dao.APIResourceDAO;
import org.wso2.carbon.is.migration.service.v700.dao.ApplicationDAO;
import org.wso2.carbon.is.migration.service.v700.dao.ScopeDAO;
import org.wso2.carbon.is.migration.service.v700.model.Application;
import org.wso2.carbon.is.migration.service.v700.util.Utils;
import org.wso2.carbon.is.migration.util.UserStoreOperationsUtil;
import org.wso2.carbon.registry.core.RegistryConstants;
import org.wso2.carbon.user.api.Tenant;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import static org.wso2.carbon.is.migration.service.v700.constant.MigratorConstants.APP_PERMISSION_STRING_SCOPE_MAPPED_API;

public class PermissionStringMigrationService {

    private final ApplicationDAO applicationDAO = new ApplicationDAO();
    private final APIResourceDAO apiResourceDAO = new APIResourceDAO();
    private final ScopeDAO scopeDAO = new ScopeDAO();

    public void execute() throws MigrationClientException {

        Set<Tenant> tenants = new HashSet<>(Arrays.asList(UserStoreOperationsUtil.getAllTenants()));
        Map<String, String> permissionScopeMapping = Utils.loadOauthScopeBinding();
        if (permissionScopeMapping == null) {
            return;
        }
        String basePermissionPath = RegistryConstants.PATH_SEPARATOR + CarbonConstants.UI_PERMISSION_NAME +
                RegistryConstants.PATH_SEPARATOR + "applications" + RegistryConstants.PATH_SEPARATOR;

        for (Tenant tenant : tenants) {
            if (apiResourceDAO.getAPIResourceIDByIdentifier(APP_PERMISSION_STRING_SCOPE_MAPPED_API, tenant.getId()) != null) {
                continue;
            }
            List<Application> applications = applicationDAO.getApplicationsExceptSystemApps(tenant.getId());
            RegistryBasedApplicationPermissionProvider registryBasedApplicationPermissionProvider = new RegistryBasedApplicationPermissionProvider();
            List<String> scopeMappedPermissions = new ArrayList<>();
            for (Application application : applications) {
                try {
                    List<ApplicationPermission> applicationPermissions = registryBasedApplicationPermissionProvider
                            .loadPermissions(application.getAppName());
                    applicationPermissions = applicationPermissions.stream()
                            .filter(applicationPermission -> permissionScopeMapping.containsKey(basePermissionPath + application.getAppName() + RegistryConstants.PATH_SEPARATOR + applicationPermission.getValue()))
                            .collect(Collectors.toList());
                    if (!applicationPermissions.isEmpty()) {
                        scopeMappedPermissions.addAll(applicationPermissions.stream().map(permission -> basePermissionPath + application.getAppName() + RegistryConstants.PATH_SEPARATOR + permission.getValue())
                                .collect(Collectors.toList()));
                    }
                } catch (IdentityApplicationManagementException e) {
                    throw new MigrationClientException("Error while retrieving registry based application permissions.", e);
                }
            }
            if (scopeMappedPermissions.isEmpty()) {
                return;
            }

            List<Scope> scopesList = new ArrayList<>();
            for (Map.Entry<String, String> entry : permissionScopeMapping.entrySet()) {
                if (scopeMappedPermissions.contains(entry.getKey())) {
                    Scope scope = new Scope.ScopeBuilder()
                            .name(entry.getValue())
                            .displayName(entry.getValue())
                            .description(entry.getValue())
                            .build();
                    scopesList.add(scope);
                }
            }
            // Create default OAuth2 resource.
            String apiId = UUID.randomUUID().toString();
            apiResourceDAO.createAPIResource(apiId, "Application Permission Mapped Resource",
                    APP_PERMISSION_STRING_SCOPE_MAPPED_API, tenant.getId(),
                    "This is Application permission string to scope mapped Resource Representation", "BUSINESS", true);
            scopeDAO.insertScopes(apiId, scopesList, tenant.getId());

            List<String> scopeIDs = apiResourceDAO.getScopeIDsByAPIId(apiId);
            for (Application application : applications) {
                applicationDAO.authorizeAPIToApplication(application.getUUID(), apiId, "RBAC");
                applicationDAO.authorizeScopesToApplication(application.getUUID(), apiId, scopeIDs);
            }
        }
    }
}
