/*
 * Copyright (c) 2023, 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.v700.migrator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.identity.core.migrate.MigrationClientException;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.is.migration.service.SchemaMigrator;
import org.wso2.carbon.is.migration.util.Constant;
import org.wso2.carbon.is.migration.util.ReportUtil;
import org.wso2.carbon.is.migration.util.Schema;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

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

/**
 * Migrator to make OAuth client IDs tenant unique.
 */
public class ClientIdTenantUnificationMigrator extends SchemaMigrator {

    private static final Logger LOG = LoggerFactory.getLogger(ClientIdTenantUnificationMigrator.class);
    private static final String CHECK_FOREIGN_KEY_VIOLATION_IN_IDN_OIDC_PROPERTY = "SELECT DISTINCT " +
            "iop.CONSUMER_KEY, iop.TENANT_ID FROM IDN_OIDC_PROPERTY iop LEFT JOIN IDN_OAUTH_CONSUMER_APPS ioca " +
            "ON iop.TENANT_ID = ioca.TENANT_ID AND iop.CONSUMER_KEY = ioca.CONSUMER_KEY " +
            "WHERE ioca.TENANT_ID IS NULL";
    private static final String CONSUMER_KEY = "CONSUMER_KEY";
    private static final String TENANT_ID = "TENANT_ID";

    @Override
    public void migrate() throws MigrationClientException {

    }

    @Override
    public void dryRun() throws MigrationClientException {

        LOG.info(Constant.MIGRATION_LOG + "Executing dry run for {}", this.getClass().getName());
        Properties migrationProperties = getMigratorConfig().getParameters();
        String reportPath = (String) migrationProperties.get(REPORT_PATH);
        ReportUtil reportUtil = new ReportUtil(reportPath);

        try {
            reportUtil.writeMessage("\n--- Summary of the report ---\n");
            LOG.info(Constant.MIGRATION_LOG + "Started the dry run for {} schema migration.",
                    this.getClass().getName());

            reportUtil.writeMessage("Checking for tenant qualified URLs and tenanted sessions.");
            LOG.info(Constant.MIGRATION_LOG + "Checking for tenant qualified URLs and tenanted sessions.");

            if (IdentityTenantUtil.isTenantQualifiedUrlsEnabled() && IdentityTenantUtil.isTenantedSessionsEnabled()) {
                reportUtil.writeMessage("No issues detected with the tenant qualified URLs and tenanted sessions.");
                LOG.info(Constant.MIGRATION_LOG + "No issues detected with the tenant qualified URLs and " +
                        "tenanted sessions.");
            } else {
                reportUtil.writeMessage("Tenant qualified URLs and/or tenanted sessions are not enabled. " +
                        "Please enable them before proceeding with the migration.");
                LOG.error(Constant.MIGRATION_LOG + "Tenant qualified URLs and/or tenanted sessions are not enabled. " +
                        "Please enable them before proceeding with the migration.");
            }

            reportUtil.writeMessage("Checking for foreign key violations for the schema migration.");
            LOG.info(Constant.MIGRATION_LOG + "Checking for foreign key violations for the schema migration.");

            try (Connection connection = getDataSource(Schema.IDENTITY.getName()).getConnection();
                 Statement statement = connection.createStatement(
                         ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
                 ResultSet resultSet = statement.executeQuery(CHECK_FOREIGN_KEY_VIOLATION_IN_IDN_OIDC_PROPERTY)) {

                if (!resultSet.isBeforeFirst()) {
                    reportUtil.writeMessage("No issues detected with the schema migration.");
                    LOG.info(Constant.MIGRATION_LOG + "No issues detected with the schema migration.");
                } else {
                    reportUtil.writeMessage(String.format("\n%-10s | %-80s", TENANT_ID, CONSUMER_KEY));
                    while (resultSet.next()) {
                        reportUtil.writeMessage(String.format(
                                "%-10d | %-80s", resultSet.getInt(TENANT_ID), resultSet.getString(CONSUMER_KEY)));
                    }
                    reportUtil.writeMessage("\nAbove listed malformed entries were found in the IDN_OIDC_PROPERTY " +
                            "table that prevents the migration client from \nsuccessfully completing the migration " +
                            "task. This was created due to a bug identified in our code and you need to manually " +
                            "remove them before proceeding with the migration. \nPlease refer to the " +
                            "git issue (https://github.com/wso2/product-is/issues/17738) for further details.");
                    LOG.error(Constant.MIGRATION_LOG + "Above listed malformed entries were found in the " +
                            "IDN_OIDC_PROPERTY table that prevents the migration client from successfully " +
                            "completing the migration task. This was created due to a bug identified in our code " +
                            "and you need to manually remove them before proceeding with the migration. Please " +
                            "refer to the git issue (https://github.com/wso2/product-is/issues/17738) for " +
                            "further details.");
                }
            } catch (SQLException e) {
                reportUtil.writeMessage("An error occurred while reading IDN_OIDC_PROPERTY data.");
                throw new MigrationClientException("An error occurred while reading IDN_OIDC_PROPERTY data.", e);
            }
            reportUtil.commit();
        } catch (IOException e) {
            throw new MigrationClientException("Error while writing the dry run report.", e);
        }
    }
}
