/**
 * Copyright © 2012-2025 by Data Virtuality GmbH
 */
package org.teiid.core.crypto;

import org.teiid.core.plugin.CorePlugin;

import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Utility class for FIPS-related functionality in the common-core module.
 * This avoids circular dependencies with the client module.
 *
 * @author Roman Pedchenko
 * @date 2025.08.07
 */
public class FipsUtil {
    public enum FipsMode {
        enabled,
        disabled;

        public boolean isEnabled() {
            return this != disabled;
        }

        public boolean isStrict() {
            return this.isEnabled();
        }

        public static FipsMode of(String fips) {
            if (fips == null || fips.trim().isEmpty()) {
                return FipsMode.disabled;
            }
            try {
                return valueOf(fips.trim().toLowerCase());
            } catch (IllegalArgumentException e) {
                return FipsMode.disabled;
            }
        }
    }
    public static final String STRICT = "strict";

	public static final String BCFIPS_PROVIDER = "BCFIPS";
	public static final String BCJSSE_PROVIDER = "BCJSSE";
	public static final String BC_KEYSTORE_TYPE = "BCFKS";

    private static final Logger LOGGER = Logger.getLogger(FipsUtil.class.getName());
    
    private static boolean fipsModeEnabled = false;
    
    private static String currentFipsProvider = null;
    
    public static synchronized void setFipsMode(boolean enabled, String providerName) {
        fipsModeEnabled = enabled;
        currentFipsProvider = enabled ? providerName : null;
    }
    
    public static boolean isFipsModeEnabled() {
        return fipsModeEnabled;
    }
    
    public static String getCurrentFipsProvider() {
        return currentFipsProvider;
    }
    
    public static SecureRandom getSecureRandom() throws CryptoException {
        try {
            if (isFipsModeEnabled() && getCurrentFipsProvider() != null) {
                return SecureRandom.getInstance("DEFAULT", getCurrentFipsProvider());
            } else {
                return new SecureRandom();
            }
        } catch (Exception e) {
            LOGGER.log(Level.WARNING, CorePlugin.Util.getString("fips_secure_random_warn", e.getMessage()));
            throw new CryptoException(e, CorePlugin.Util.getString("fips_secure_random_error", e.getMessage()));
        }
    }
    
    public static void validateProvider(String providerName) throws CryptoException {
        if (providerName == null) {
            throw new CryptoException(CorePlugin.Util.getString("fips_provider_name_null"));
        }
        
        Provider provider = Security.getProvider(providerName);
        if (provider == null) {
            throw new CryptoException(CorePlugin.Util.getString("fips_provider_not_found", providerName));
        }
        
        if (BCFIPS_PROVIDER.equals(providerName)) {
            try {
                Class<?> fipsStatusClass = Class.forName("org.bouncycastle.crypto.fips.FipsStatus");
                Object isReady = fipsStatusClass.getMethod("isReady").invoke(null);
                if (isReady instanceof Boolean && !((Boolean) isReady)) {
                    throw new CryptoException(CorePlugin.Util.getString("fips_bcfips_not_ready"));
                }
            } catch (ClassNotFoundException e) {
                // Older version of BC FIPS might not have this class
                LOGGER.log(Level.WARNING, CorePlugin.Util.getString("fips_could_not_verify_status", e.getMessage()));
            } catch (Exception e) {
                throw new CryptoException(e, CorePlugin.Util.getString("fips_bcfips_error_validating", e.getMessage()));
            }
        }
    }
}
