/*
 * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
 *
 * WSO2 LLC. licenses this file to you 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.
 */

package org.wso2.carbon.is.migration.service.v710.migrator;

import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.base.IdentityRuntimeException;
import org.wso2.carbon.identity.core.migrate.MigrationClientException;
import org.wso2.carbon.identity.core.persistence.registry.RegistryResourceMgtService;
import org.wso2.carbon.identity.core.persistence.registry.RegistryResourceMgtServiceImpl;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.governance.exceptions.notiification.NotificationTemplateManagerServerException;
import org.wso2.carbon.identity.governance.model.NotificationTemplate;
import org.wso2.carbon.identity.governance.service.notification.NotificationChannels;
import org.wso2.carbon.identity.organization.management.service.model.BasicOrganization;
import org.wso2.carbon.is.migration.service.Migrator;
import org.wso2.carbon.is.migration.service.v710.dao.NotificationDAO;
import org.wso2.carbon.is.migration.service.v710.dao.OrganizationDAO;
import org.wso2.carbon.is.migration.service.v710.model.NotificationTemplateKey;
import org.wso2.carbon.is.migration.service.v710.model.ScenarioTemplatesDTO;
import org.wso2.carbon.registry.core.Collection;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.user.api.Tenant;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import static org.wso2.carbon.base.MultitenantConstants.SUPER_TENANT_ID;
import static org.wso2.carbon.is.migration.service.v710.constants.NotificationRegistryConstants.EMAIL_TEMPLATE_PATH;
import static org.wso2.carbon.is.migration.service.v710.constants.NotificationRegistryConstants.SMS_TEMPLATE_PATH;
import static org.wso2.carbon.is.migration.service.v710.constants.NotificationRegistryConstants.TEMPLATE_CONTENT_TYPE;
import static org.wso2.carbon.is.migration.service.v710.constants.NotificationRegistryConstants.TEMPLATE_DISPLAY_NAME;
import static org.wso2.carbon.is.migration.service.v710.constants.NotificationRegistryConstants.TEMPLATE_LOCALE;
import static org.wso2.carbon.is.migration.service.v710.constants.NotificationRegistryConstants.TEMPLATE_TYPE;
import static org.wso2.carbon.is.migration.service.v710.constants.NotificationRegistryConstants.TEMPLATE_TYPE_DISPLAY_NAME;
import static org.wso2.carbon.registry.core.RegistryConstants.PATH_SEPARATOR;

/**
 * Migration implementation to migrate organization template inheritance along with registry to DB migration.
 */
public class B2BOrgNotificationTemplateMigrator extends Migrator {

    private static final Logger LOG = LoggerFactory.getLogger(B2BOrgNotificationTemplateMigrator.class);
    private static final String LOG_PREFIX = "B2B-NTM>> ";
    private static final String WAIT_TIME = "waitTime";
    private static final long DEFAULT_WAIT_TIME_IN_MILLIS = 500;

    private static final OrganizationDAO organizationDAO = new OrganizationDAO();
    private static final NotificationDAO notificationDAO = new NotificationDAO();
    private final RegistryResourceMgtService resourceMgtService = new RegistryResourceMgtServiceImpl();
    private long waitTimeInMillis = DEFAULT_WAIT_TIME_IN_MILLIS;

    @Override
    public void dryRun() throws MigrationClientException {

        LOG.info("Dry run capability not implemented in {} migrator.", this.getClass().getName());
        // TODO: Implement dry run.
    }

    @Override
    public void migrate() throws MigrationClientException {

        migrateOrgNotificationTemplateInheritance();
    }

    private void migrateOrgNotificationTemplateInheritance() throws MigrationClientException {

        Properties migrationProperties = getMigratorConfig().getParameters();

        if (migrationProperties.containsKey(WAIT_TIME)) {
            waitTimeInMillis = (int) migrationProperties.get(WAIT_TIME);
        }
        LOG.info(LOG_PREFIX + "Parameters: Wait time {}ms.", waitTimeInMillis);

        Set<Tenant> tenants = getTenants();
        int totalTenantCount = tenants.size();
        LOG.info(LOG_PREFIX + "Found {} tenants including super tenant in the system.", totalTenantCount + 1);

        try {
            migrateForTenant(SUPER_TENANT_ID);
            int i = 0;
            for (Tenant tenant : tenants) {
                i++;
                LOG.info(LOG_PREFIX + "Starting migration for {} of {} tenant.", i, totalTenantCount);

                int tenantId = tenant.getId();
                String tenantDomain = tenant.getDomain();
                LOG.info(LOG_PREFIX + "Migrating tenant: {} with tid: {}.", tenantDomain, tenantId);
                try {
                    PrivilegedCarbonContext.startTenantFlow();
                    PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
                    carbonContext.setTenantId(tenantId);
                    carbonContext.setTenantDomain(tenantDomain);
                    migrateForTenant(tenantId);
                } catch (MigrationClientException e) {
                    if (!isContinueOnError()) {
                        throw new NotificationTemplateManagerServerException(e.getMessage(), e);
                    }
                } finally {
                    PrivilegedCarbonContext.endTenantFlow();
                }
            }
        } catch (NotificationTemplateManagerServerException e) {
            LOG.error("Error while migrating notification templates.", e);
        }
    }

    private void migrateForTenant(int tenantId) throws MigrationClientException {

        try {
            if (tenantId != SUPER_TENANT_ID) {
                IdentityTenantUtil.getTenantRegistryLoader().loadTenantRegistry(tenantId);
            }
            String tenantDomain = IdentityTenantUtil.getTenantDomain(tenantId);
            migrateOrgNotificationTemplateInheritanceForChannel(NotificationChannels.EMAIL_CHANNEL.getChannelType(),
                    tenantDomain);
            migrateOrgNotificationTemplateInheritanceForChannel(NotificationChannels.SMS_CHANNEL.getChannelType(),
                    tenantDomain);
        } catch (RegistryException | NotificationTemplateManagerServerException e) {
            throw new MigrationClientException("Error while migrating tenant with id: " + tenantId, e);
        }
    }

    private void migrateOrgNotificationTemplateInheritanceForChannel(String channel, String tenantDomain)
            throws NotificationTemplateManagerServerException {

        String organizationId;
        try {
            organizationId = resolveOrganizationId(tenantDomain);
            if (organizationId == null) {
                LOG.info("No organization found for tenant: {}. Hence skipping the migration.", tenantDomain);
                return;
            }
        } catch (MigrationClientException e) {
            throw new NotificationTemplateManagerServerException("Error while resolving organization id for tenant: " +
                    tenantDomain, e);
        }

        // Get all the org notification templates available in database in the root organization.
        List<NotificationTemplate> notificationTemplatesFromDB = getOrgNotificationTemplatesFromDB(channel,
                tenantDomain);
        // Get all the org notification templates available in registry in the root organization.
        Map<String, ScenarioTemplatesDTO> notificationTemplatesFromRegistry = getOrgNotificationTemplatesFromRegistry(
                channel, tenantDomain);

        Map<NotificationTemplateKey, NotificationTemplate> allTemplates = new HashMap<>();

        for (NotificationTemplate notificationTemplate : notificationTemplatesFromDB) {
            String typeKey = notificationTemplate.getType();
            String localeKey = notificationTemplate.getLocale().toLowerCase();
            String notificationChannel = notificationTemplate.getNotificationChannel();
            NotificationTemplateKey notificationTemplateKey = new NotificationTemplateKey(typeKey, localeKey,
                    notificationChannel);
            allTemplates.put(notificationTemplateKey, notificationTemplate);
        }

        for (Map.Entry<String, ScenarioTemplatesDTO> entry : notificationTemplatesFromRegistry.entrySet()) {
            String scenarioName = entry.getKey();
            ScenarioTemplatesDTO scenarioTemplates = entry.getValue();
            List<NotificationTemplate> orgTemplates = scenarioTemplates.getOrgTemplates();
            for (NotificationTemplate notificationTemplate : orgTemplates) {
                String typeKey = notificationTemplate.getType();
                String localeKey = notificationTemplate.getLocale().toLowerCase();
                String notificationChannel = notificationTemplate.getNotificationChannel();
                NotificationTemplateKey notificationTemplateKey = new NotificationTemplateKey(typeKey, localeKey,
                        notificationChannel);
                // Ignoring the template if it is already added from the database.
                if (!allTemplates.containsKey(notificationTemplateKey)) {
                    allTemplates.put(notificationTemplateKey, notificationTemplate);
                }
            }
        }

        // Migrate all the child organizations under root org hierarchy starting from immediate child organizations.
        migrateForChildOrganizations(channel, organizationId, allTemplates);
    }

    private void migrateForChildOrganizations(String channel,
                                              String parentOrganization,
                                              Map<NotificationTemplateKey, NotificationTemplate> parentOrgTemplates)
            throws NotificationTemplateManagerServerException {

        List<BasicOrganization> childOrganizations;
        try {
            childOrganizations = getChildOrganizations(parentOrganization);
        } catch (MigrationClientException e) {
            throw new NotificationTemplateManagerServerException(
                    "Error while getting child organizations for parent: " + parentOrganization, e);
        }

        // Iterating the immediate child organizations of passed parent organization.
        for (BasicOrganization organization : childOrganizations) {
            String organizationId = organization.getId();
            String tenantDomain;
            try {
                tenantDomain = resolveTenantDomain(organizationId);
            } catch (MigrationClientException e) {
                throw new NotificationTemplateManagerServerException(
                        "Error while resolving tenant domain for organization: " + organizationId, e);
            }

            // Get all the org notification templates available in database in the child organization.
            List<NotificationTemplate> notificationTemplatesFromDBInChildOrg = getOrgNotificationTemplatesFromDB(
                    channel, tenantDomain);
            // Get all the org notification templates available in registry in the child organization.
            Map<String, ScenarioTemplatesDTO> notificationTemplatesFromRegistryInChildOrg =
                    getOrgNotificationTemplatesFromRegistry(channel, tenantDomain);

            // Categorize the retrieved org notification templates based on the storage.
            Map<NotificationTemplateKey, NotificationTemplate> templatesInDB = new HashMap<>();
            Map<NotificationTemplateKey, NotificationTemplate> templatesInRegistry = new HashMap<>();
            Map<NotificationTemplateKey, NotificationTemplate> templatesInBothStores = new HashMap<>();

            for (NotificationTemplate notificationTemplate : notificationTemplatesFromDBInChildOrg) {
                String displayName = notificationTemplate.getDisplayName();
                String typeKey = notificationTemplate.getType();
                String localeKey = notificationTemplate.getLocale().toLowerCase();
                String notificationChannel = notificationTemplate.getNotificationChannel();
                NotificationTemplateKey notificationTemplateKey = new NotificationTemplateKey(typeKey, localeKey,
                        notificationChannel);

                // Check if this template exists in the registry.
                if (notificationTemplatesFromRegistryInChildOrg.containsKey(displayName)) {
                    List<NotificationTemplate> registryTemplates = notificationTemplatesFromRegistryInChildOrg
                            .get(displayName).getOrgTemplates();
                    boolean foundInRegistry = registryTemplates.stream().anyMatch(
                            registryTemplate -> registryTemplate.getLocale().toLowerCase().equals(localeKey)
                                    && registryTemplate.getNotificationChannel().equals(notificationChannel));
                    if (foundInRegistry) {
                        templatesInBothStores.put(notificationTemplateKey, notificationTemplate);
                    } else {
                        templatesInDB.put(notificationTemplateKey, notificationTemplate);
                    }
                } else {
                    templatesInDB.put(notificationTemplateKey, notificationTemplate);
                }
            }

            for (Map.Entry<String, ScenarioTemplatesDTO> entry :
                    notificationTemplatesFromRegistryInChildOrg.entrySet()) {
                String scenarioName = entry.getKey();
                ScenarioTemplatesDTO scenarioTemplates = entry.getValue();
                List<NotificationTemplate> registryTemplates = scenarioTemplates.getOrgTemplates();
                for (NotificationTemplate registryTemplate : registryTemplates) {
                    String typeKey = registryTemplate.getType();
                    String localeKey = registryTemplate.getLocale().toLowerCase();
                    String notificationChannel = registryTemplate.getNotificationChannel();
                    NotificationTemplateKey registryTemplateKey = new NotificationTemplateKey(typeKey, localeKey,
                            notificationChannel);

                    // Check if this template exists in the database.
                    boolean foundInDB = notificationTemplatesFromDBInChildOrg.stream().anyMatch(
                            dbTemplate -> dbTemplate.getDisplayName().equals(scenarioName)
                                    && dbTemplate.getLocale().toLowerCase().equals(localeKey)
                                    && dbTemplate.getNotificationChannel().equals(notificationChannel));
                    if (!foundInDB) {
                        templatesInRegistry.put(registryTemplateKey, registryTemplate);
                    }
                }
            }

            /*
            Execute org template inheritance migration and registry to database migration on categorized
            notification templates.
             */

            /*
            This block migrates the templates that only available in registry. First the templates reflecting
            inheritance should be removed and then remaining templates should be migrated to database.
            */
            for (Map.Entry<NotificationTemplateKey, NotificationTemplate> entry : templatesInRegistry.entrySet()) {

                NotificationTemplateKey templateKey = entry.getKey();
                NotificationTemplate notificationTemplate = entry.getValue();

                if (parentOrgTemplates.containsKey(templateKey)) {
                    NotificationTemplate parentTemplate = parentOrgTemplates.get(templateKey);
                    if (areNotificationTemplatesEqual(notificationTemplate, parentTemplate)) {
                        /*
                        Since parent org template and child org template are equal. This template should be migrated to
                        reflect the inheritance. Hence, the template needs to be deleted from the registry.
                         */
                        removeTemplateFromRegistry(notificationTemplate, tenantDomain);
                        continue;
                    }
                }
                /*
                Reaching this point means either there is no notification template for the given scenario name, type and
                channel in parent org or parent template and child template are not equal. In this case, existing
                template should not be removed. Since the notification template storage is registry, adding the
                notification template to database as a part of registry to database migration.
                 */
                addTemplateToDB(notificationTemplate, tenantDomain);
            }

            /*
            This block migrates the templates that only available in both registry and database. Here, only removing the
            templates reflecting inheritance from both registry and database will be enough since database migration is
            not needed.
            */
            for (Map.Entry<NotificationTemplateKey, NotificationTemplate> entry : templatesInBothStores.entrySet()) {

                NotificationTemplateKey templateKey = entry.getKey();
                NotificationTemplate notificationTemplate = entry.getValue();

                if (parentOrgTemplates.containsKey(templateKey)) {
                    NotificationTemplate parentTemplate = parentOrgTemplates.get(templateKey);
                    if (areNotificationTemplatesEqual(notificationTemplate, parentTemplate)) {
                        /*
                        Since parent org template and child org template are equal. This template should be migrated to
                        reflect the inheritance. Hence, the template needs to be deleted from both database and
                        registry.
                         */
                        removeTemplateFromDB(notificationTemplate, tenantDomain);
                        removeTemplateFromRegistry(notificationTemplate, tenantDomain);
                    }
                }
            }

            /*
            This block migrates the templates that only available in database. Here, only removing the templates
            reflecting inheritance from the database will be enough since templates are already available in database.
            */
            for (Map.Entry<NotificationTemplateKey, NotificationTemplate> entry : templatesInDB.entrySet()) {

                NotificationTemplateKey templateKey = entry.getKey();
                NotificationTemplate notificationTemplate = entry.getValue();

                if (parentOrgTemplates.containsKey(templateKey)) {
                    NotificationTemplate parentTemplate = parentOrgTemplates.get(templateKey);
                    if (areNotificationTemplatesEqual(notificationTemplate, parentTemplate)) {
                        /*
                        Since parent org template and child org template are equal. This template should be migrated to
                        reflect the inheritance. Hence, the template needs to be deleted from the registry.
                         */
                        removeTemplateFromDB(notificationTemplate, tenantDomain);
                    }
                }
            }

            // Concatenating all the templates for the migration of child organizations under current organization.
            Map<NotificationTemplateKey, NotificationTemplate> allTemplates = new HashMap<>();
            allTemplates.putAll(templatesInDB);
            allTemplates.putAll(templatesInRegistry);
            allTemplates.putAll(templatesInBothStores);

            if (waitTimeInMillis > 0) {
                try {
                    LOG.info(LOG_PREFIX + "Waiting {}ms to cool down...", waitTimeInMillis);
                    Thread.sleep(waitTimeInMillis);
                } catch (InterruptedException e) {
                    LOG.error(LOG_PREFIX + "Error while waiting for cool down.", e);
                }
            }

            /*
            Recursively migrate all the child organizations under current organization starting from immediate child
            organizations of current organization.
             */
            migrateForChildOrganizations(channel, organizationId, allTemplates);
        }
    }

    private List<NotificationTemplate> getOrgNotificationTemplatesFromDB(String channel, String tenantDomain)
            throws NotificationTemplateManagerServerException {

        int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
        return notificationDAO.listOrgNotificationTemplates(channel, tenantId);
    }

    private Map<String, ScenarioTemplatesDTO> getOrgNotificationTemplatesFromRegistry(String channel,
                                                                                      String tenantDomain)
            throws NotificationTemplateManagerServerException {

        String regPathForChannel = EMAIL_TEMPLATE_PATH;
        if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(channel)) {
            regPathForChannel = SMS_TEMPLATE_PATH;
        }

        Resource resource;
        try {
            resource = resourceMgtService.getIdentityResource(regPathForChannel, tenantDomain);
            if (resource == null) {
                LOG.info(LOG_PREFIX + "No {} templates found in tenant {}.", channel, tenantDomain);
                return new HashMap<>();
            }
        } catch (IdentityRuntimeException e) {
            String msg = String.format("Error while checking the existence of %s templates in tenant %s.", channel,
                    tenantDomain);
            throw new NotificationTemplateManagerServerException(msg, e);
        }

        return getTemplatesFromRegistry(regPathForChannel, channel, tenantDomain);
    }

    private Map<String, ScenarioTemplatesDTO> getTemplatesFromRegistry(String regPathForChannel,
                                                                       String notificationChannel,
                                                                       String tenantDomain)
            throws NotificationTemplateManagerServerException {

        try {
            Resource regResourceForChannel = resourceMgtService.getIdentityResource(regPathForChannel, tenantDomain);
            if (!(regResourceForChannel instanceof Collection)) {
                String message = String.format(
                        "Invalid registry resource found while trying to list %s notification templates in tenant %s.",
                        notificationChannel, tenantDomain);
                throw new NotificationTemplateManagerServerException(message);
            }

            Collection regCollectionForChannel = (Collection) regResourceForChannel;
            Map<String, ScenarioTemplatesDTO> templates = new HashMap<>();
            for (String regPathForScenario : regCollectionForChannel.getChildren()) {
                Resource templateTypeResource =
                        resourceMgtService.getIdentityResource(regPathForScenario, tenantDomain);
                String templateTypeDisplayName = templateTypeResource.getProperty(TEMPLATE_DISPLAY_NAME);
                ScenarioTemplatesDTO templatesForScenario =
                        getTemplatesForScenario(templateTypeResource, templateTypeDisplayName, notificationChannel,
                                tenantDomain);
                templates.put(templateTypeDisplayName, templatesForScenario);
            }
            return templates;
        } catch (RegistryException | IdentityRuntimeException e) {
            String errorMessage =
                    String.format("Error while listing %s notification templates in tenant %s.", notificationChannel,
                            tenantDomain);
            throw new NotificationTemplateManagerServerException(errorMessage, e);
        }
    }

    private ScenarioTemplatesDTO getTemplatesForScenario(Resource templateTypeResource, String scenarioType,
                                                         String channel, String tenantDomain)
            throws RegistryException, NotificationTemplateManagerServerException {

        if (!(templateTypeResource instanceof Collection)) {
            String message = String.format(
                    "Invalid registry resource found while trying to list %s notification templates for scenario %s " +
                            "in tenant %s.", channel, scenarioType, tenantDomain);
            throw new NotificationTemplateManagerServerException(message);
        }

        Collection templateTypeCollection = (Collection) templateTypeResource;
        List<NotificationTemplate> orgTemplates =
                getTemplateLocaleList(templateTypeCollection, channel, tenantDomain);

        return new ScenarioTemplatesDTO(scenarioType, orgTemplates, null);
    }

    private List<NotificationTemplate> getTemplateLocaleList(Collection templateCollection, String channel,
                                                             String tenantDomain)
            throws RegistryException, NotificationTemplateManagerServerException {

        List<NotificationTemplate> templateLocaleList = new ArrayList<>();
        for (String childPath : templateCollection.getChildren()) {
            Resource templateResource = resourceMgtService.getIdentityResource(childPath, tenantDomain);
            if (!(templateResource instanceof Collection)) {
                NotificationTemplate notificationTemplate = getNotificationTemplate(templateResource, channel);
                templateLocaleList.add(notificationTemplate);
            } else {
                String message = String.format(LOG_PREFIX +
                                "Invalid reg collection %s found when building %s template locale list in tenant %s.",
                        childPath, channel, tenantDomain);
                LOG.warn(message);
            }
        }
        return templateLocaleList;
    }

    private NotificationTemplate getNotificationTemplate(Resource templateResource, String notificationChannel)
            throws NotificationTemplateManagerServerException {

        NotificationTemplate notificationTemplate = new NotificationTemplate();

        // Get template meta properties.
        String displayName = templateResource.getProperty(TEMPLATE_TYPE_DISPLAY_NAME);
        String type = templateResource.getProperty(TEMPLATE_TYPE);
        String locale = templateResource.getProperty(TEMPLATE_LOCALE);

        if (NotificationChannels.EMAIL_CHANNEL.getChannelType().equals(notificationChannel)) {
            String contentType = templateResource.getProperty(TEMPLATE_CONTENT_TYPE);
            notificationTemplate.setContentType(contentType);
        }

        notificationTemplate.setDisplayName(displayName);
        notificationTemplate.setType(type);
        notificationTemplate.setLocale(locale);
        // Set template subject, body and footer.
        setTemplateElements(templateResource, notificationTemplate, notificationChannel, displayName, locale);
        notificationTemplate.setNotificationChannel(notificationChannel);
        return notificationTemplate;
    }

    private void setTemplateElements(Resource templateResource, NotificationTemplate notificationTemplate,
                                     String notificationChannel, String displayName, String locale)
            throws NotificationTemplateManagerServerException {

        try {
            Object content = templateResource.getContent();
            if (content != null) {
                byte[] templateContentArray = (byte[]) content;
                String templateContent = new String(templateContentArray, StandardCharsets.UTF_8);
                String[] templateContentElements = new Gson().fromJson(templateContent, String[].class);

                // Validate template content.
                if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(notificationChannel)) {
                    if (templateContentElements != null && templateContentElements.length == 1) {
                        notificationTemplate.setBody(templateContentElements[0]);
                    } else {
                        String message =
                                String.format("Invalid SMS template content found in the template: %s in locale: %s.",
                                        displayName, locale);
                        throw new NotificationTemplateManagerServerException(message);
                    }
                } else {
                    if (templateContentElements != null && templateContentElements.length == 3) {
                        notificationTemplate.setSubject(templateContentElements[0]);
                        notificationTemplate.setBody(templateContentElements[1]);
                        notificationTemplate.setFooter(templateContentElements[2]);
                    } else {
                        String message =
                                String.format("Invalid email template content found in the template: %s in locale: %s.",
                                        displayName, locale);
                        throw new NotificationTemplateManagerServerException(message);
                    }
                }
            } else {
                String message =
                        String.format("No content found in the template: %s in locale: %s.", displayName, locale);
                throw new NotificationTemplateManagerServerException(message);
            }
        } catch (RegistryException e) {
            String message = String.format("Error while getting the content of the template: %s in locale: %s.",
                    displayName, locale);
            throw new NotificationTemplateManagerServerException(message, e);
        }
    }

    private void removeTemplateFromDB(NotificationTemplate notificationTemplate, String tenantDomain)
            throws NotificationTemplateManagerServerException {

        int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
        notificationDAO.removeOrgNotificationTemplate(notificationTemplate, tenantId);
    }

    private void removeTemplateFromRegistry(NotificationTemplate notificationTemplate, String tenantDomain)
            throws NotificationTemplateManagerServerException {

        String channel = notificationTemplate.getNotificationChannel();
        String regPathForChannel = EMAIL_TEMPLATE_PATH;
        if (NotificationChannels.SMS_CHANNEL.getChannelType().equals(channel)) {
            regPathForChannel = SMS_TEMPLATE_PATH;
        }

        String displayName = notificationTemplate.getDisplayName();
        String locale = notificationTemplate.getLocale();
        String path = regPathForChannel + PATH_SEPARATOR + getNormalizedName(displayName);
        try {
            resourceMgtService.deleteIdentityResource(path, tenantDomain, locale);
        } catch (IdentityRuntimeException e) {
            String msg = String.format(
                    "Error while deleting %s template with %s locale for %s notification channel in tenant %s.",
                    displayName, locale, channel, tenantDomain);
            throw new NotificationTemplateManagerServerException(msg, e);
        }
    }

    private String getNormalizedName(String templateTypeName) {

        if (StringUtils.isNotBlank(templateTypeName)) {
            return templateTypeName.replaceAll("\\s+", "").toLowerCase();
        }
        throw new IllegalArgumentException("Invalid template type name provided : " + templateTypeName);
    }

    private void addTemplateToDB(NotificationTemplate notificationTemplate, String tenantDomain)
            throws NotificationTemplateManagerServerException {

        String type = notificationTemplate.getType();
        String channel = notificationTemplate.getNotificationChannel();
        String displayName = notificationTemplate.getDisplayName();
        int tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
        if (!notificationDAO.isExistNotificationTemplateType(type, channel, tenantId)) {
            notificationDAO.addNotificationTemplateType(type, displayName, channel, tenantId);
        }
        notificationDAO.addNotificationTemplate(notificationTemplate, tenantId);
    }

    private boolean areNotificationTemplatesEqual(NotificationTemplate template1, NotificationTemplate template2) {

        return StringUtils.equals(template1.getDisplayName(), template2.getDisplayName()) &&
                StringUtils.equals(template1.getLocale(), template2.getLocale()) &&
                StringUtils.equals(template1.getNotificationChannel(), template2.getNotificationChannel()) &&
                StringUtils.equals(template1.getContentType(), template2.getContentType()) &&
                StringUtils.equals(template1.getBody(), template2.getBody()) &&
                StringUtils.equals(template1.getSubject(), template2.getSubject()) &&
                StringUtils.equals(template1.getFooter(), template2.getFooter());
    }

    private String resolveTenantDomain(String organizationId) throws MigrationClientException {

        return organizationDAO.resolveTenantDomain(organizationId);
    }

    private String resolveOrganizationId(String tenantDomain) throws MigrationClientException {

        return organizationDAO.resolveOrganizationId(tenantDomain);
    }

    private List<BasicOrganization> getChildOrganizations(String parentOrganizationId) throws MigrationClientException {

        return organizationDAO.getChildOrganizations(parentOrganizationId);
    }
}
