/**
 * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com).
 *
 * 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.migrator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.identity.core.migrate.MigrationClientException;
import org.wso2.carbon.identity.scim2.common.exceptions.IdentitySCIMException;
import org.wso2.carbon.identity.scim2.common.group.SCIMGroupHandler;
import org.wso2.carbon.is.migration.internal.ISMigrationServiceDataHolder;
import org.wso2.carbon.is.migration.service.Migrator;
import org.wso2.carbon.is.migration.service.v700.dao.OAuth2ScopeDAO;
import org.wso2.carbon.is.migration.service.v700.dao.UserCoreDAO;
import org.wso2.carbon.is.migration.util.Utility;
import org.wso2.carbon.user.api.Tenant;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.util.UserCoreUtil;

import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.wso2.carbon.is.migration.util.Constant.MIGRATION_LOG;

/**
 * This class is to handle the migration of OAuth2 scopes with group role separation.
 */
public class OAuth2ScopeGroupRoleMigrator extends Migrator {

    private static final Logger LOG = LoggerFactory.getLogger(OAuth2ScopeGroupRoleMigrator.class);

    private RealmService realmService = ISMigrationServiceDataHolder.getRealmService();

    private static final OAuth2ScopeDAO oAuth2ScopeDAO = new OAuth2ScopeDAO();

    private static final UserCoreDAO userCoreDAO = new UserCoreDAO();

    @Override
    public void dryRun() throws MigrationClientException {

    }

    @Override
    public void migrate() throws MigrationClientException {

        LOG.info(MIGRATION_LOG + "Starting the migration of OAuth2 scopes with group role separation.");
        migrateOAuth2ScopesForGroupRoleSeparation(MultitenantConstants.SUPER_TENANT_ID);
        Set<Tenant> tenants = Utility.getTenants();
        for (Tenant tenant : tenants) {
            migrateOAuth2ScopesForGroupRoleSeparation(tenant.getId());
        }
        LOG.info(MIGRATION_LOG + "Migration of OAuth2 scopes with group role separation is completed.");
    }

    private void migrateOAuth2ScopesForGroupRoleSeparation(int tenantId) throws MigrationClientException {

        LOG.info(MIGRATION_LOG + "Migrating OAuth2 scopes with group role separation for tenant: " + tenantId);
        // Get the groups with scope bindings.
        Map<String, List<String>> groupScopeMap = oAuth2ScopeDAO.getScopeBindingsOfGroups(tenantId);
        try {
            UserRealm userRealm = realmService.getTenantUserRealm(tenantId);
            UserStoreManager userStoreManager = userRealm.getUserStoreManager();
            for (Map.Entry<String, List<String>> entry : groupScopeMap.entrySet()) {
                String groupName = entry.getKey();
                String domain = UserCoreUtil.extractDomainFromName(groupName);
                boolean isDomainExist = userCoreDAO.isExistingDomain(domain, tenantId);
                // Validate the domain of the group.
                if (!isDomainExist) {
                    continue;
                }
                String groupNameWithoutDomain = UserCoreUtil.removeDomainFromName(groupName);
                String internalSystemRoleName = getInternalSystemRoleName(groupNameWithoutDomain, domain);
                // Check whether the internal system role exists.
                boolean isInternalSystemRoleExist =
                        ((AbstractUserStoreManager) userStoreManager).isExistingRole(internalSystemRoleName);
                if (!isInternalSystemRoleExist) {
                    // Create the internal system role if it does not exist.
                    userStoreManager.addRole(internalSystemRoleName, null, null);
                    // Assign the internal system role to the group.
                    ((AbstractUserStoreManager) userStoreManager)
                            .updateGroupListOfHybridRole(internalSystemRoleName, null, new String[]{groupName});
                    addSCIMGroupData(internalSystemRoleName, tenantId);
                }
                List<String> scopes = entry.getValue();
                // Add the scope bindings associated with group to internal system role.
                oAuth2ScopeDAO.addScopeBindings(internalSystemRoleName, scopes, tenantId);
            }
            LOG.info(MIGRATION_LOG + "Migrated OAuth2 scopes with group role separation for tenant: " + tenantId);
        } catch (UserStoreException e) {
            throw new MigrationClientException("Error while getting the user store manager for tenant: " + tenantId, e);
        }
    }

    private void addSCIMGroupData(String roleName, int tenantID) throws org.wso2.carbon.user.core.UserStoreException {

        SCIMGroupHandler scimGroupHandler = new SCIMGroupHandler(tenantID);
        try {
            if (!scimGroupHandler.isGroupExisting(roleName)) {
                scimGroupHandler.addMandatoryAttributes(roleName);
            }
        } catch (IdentitySCIMException e) {
            throw new org.wso2.carbon.user.core.UserStoreException(
                    "Error retrieving group information from SCIM Tables.", e);
        }
    }

    private String getInternalSystemRoleName(String groupName, String domain) {

        return UserCoreConstants.INTERNAL_DOMAIN + UserCoreConstants.DOMAIN_SEPARATOR +
                UserCoreConstants.INTERNAL_SYSTEM_ROLE_PREFIX + domain.toLowerCase().concat("_") + groupName;
    }
}
