ConfigurationIntrospector.java

package com.cloudforge.core.config;

import com.cloudforge.core.annotation.ConfigField;
import com.cloudforge.core.interfaces.ApplicationSpec;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * Discovers and filters configuration fields using reflection and annotations.
 *
 * <p>This is the core of the Configuration Introspection system, providing automatic
 * field discovery based on {@link ConfigField} annotations and application capabilities.</p>
 *
 * <h2>Usage</h2>
 * <pre>{@code
 * // Discover all fields for an application
 * List<ConfigFieldInfo> fields = ConfigurationIntrospector.discoverFields(gitlabSpec);
 *
 * // Discover fields in a specific category
 * List<ConfigFieldInfo> databaseFields = ConfigurationIntrospector.discoverFields(gitlabSpec, "database");
 *
 * // Filter by visibility
 * List<ConfigFieldInfo> visibleFields = fields.stream()
 *     .filter(f -> f.isVisible(config))
 *     .toList();
 * }</pre>
 *
 * @since 3.0.0
 */
public class ConfigurationIntrospector {

    /**
     * Discovers all configuration fields from DeploymentConfig.
     *
     * <p>Returns fields sorted by category order, then field order, then display name.</p>
     *
     * @param appSpec application spec for determining field visibility
     * @return list of all discovered fields
     */
    public static List<ConfigFieldInfo> discoverFields(ApplicationSpec appSpec) {
        return discoverFields(appSpec, null);
    }

    /**
     * Discovers configuration fields for a specific category.
     *
     * <p>Returns fields sorted by field order, then display name.</p>
     *
     * @param appSpec application spec for determining field visibility
     * @param category category to filter by, or null for all categories
     * @return list of discovered fields matching the category
     */
    public static List<ConfigFieldInfo> discoverFields(ApplicationSpec appSpec, String category) {
        List<ConfigFieldInfo> fields = new ArrayList<>();

        // Use reflection to find all fields in DeploymentConfig
        // For now, we'll scan the DeploymentConfig class
        Class<?> configClass = getDeploymentConfigClass();
        if (configClass == null) {
            return fields;
        }

        for (Field field : configClass.getDeclaredFields()) {
            if (!field.isAnnotationPresent(ConfigField.class)) {
                continue;
            }

            ConfigFieldInfo fieldInfo = ConfigFieldInfo.from(field);

            // Filter by category if specified
            if (category != null && !category.isEmpty() && !fieldInfo.category().equals(category)) {
                continue;
            }

            fields.add(fieldInfo);
        }

        // Sort by category order (if applicable), field order, then display name
        fields.sort(Comparator
            .comparing((ConfigFieldInfo f) -> getCategoryOrder(f.category()))
            .thenComparing(ConfigFieldInfo::order)
            .thenComparing(ConfigFieldInfo::displayName));

        return fields;
    }

    /**
     * Discovers visible fields only (based on application capabilities and configuration state).
     *
     * @param appSpec application spec for determining field visibility
     * @param config current configuration state for evaluating visibility expressions
     * @return list of visible fields
     */
    public static List<ConfigFieldInfo> discoverVisibleFields(ApplicationSpec appSpec, Object config) {
        return discoverFields(appSpec).stream()
            .filter(field -> field.isVisible(appSpec, config))
            .toList();
    }

    /**
     * Discovers fields by category with visibility filtering.
     *
     * @param appSpec application spec
     * @param config current configuration state
     * @param category category to filter by
     * @return list of visible fields in the category
     */
    public static List<ConfigFieldInfo> discoverVisibleFields(ApplicationSpec appSpec, Object config, String category) {
        return discoverFields(appSpec, category).stream()
            .filter(field -> field.isVisible(appSpec, config))
            .toList();
    }

    /**
     * Gets the DeploymentConfig class using reflection.
     *
     * <p>Tries multiple potential package locations for DeploymentConfig.</p>
     */
    private static Class<?> getDeploymentConfigClass() {
        // Try common package locations
        String[] packagePrefixes = {
            "com.cloudforge.core.config.",
            "com.cloudforgeci.api.core."
        };

        for (String prefix : packagePrefixes) {
            try {
                return Class.forName(prefix + "DeploymentConfig");
            } catch (ClassNotFoundException e) {
                // Try next package
            }
        }

        return null;
    }

    /**
     * Gets the display order for a category.
     *
     * <p>Categories are displayed in this order:</p>
     * <ol>
     *   <li>basic - Essential configuration</li>
     *   <li>network - Domain, SSL, networking</li>
     *   <li>security - Authentication, encryption, compliance</li>
     *   <li>database - RDS database provisioning</li>
     *   <li>resources - CPU, memory, scaling, instance sizing</li>
     *   <li>monitoring - CloudWatch, GuardDuty, logging</li>
     * </ol>
     */
    private static int getCategoryOrder(String category) {
        return switch (category) {
            case "basic" -> 1;
            case "network" -> 2;
            case "security" -> 3;
            case "database" -> 4;
            case "resources" -> 5;
            case "monitoring" -> 6;
            default -> 1000;  // Unknown categories go last
        };
    }
}