<?php
/**
 * Conditional Renderer class file
 *
 * This file contains the ConditionalRenderer class which handles complex
 * conditional evaluation logic for rendering decisions within the
 * UltraguardPro system. It supports various condition types including
 * boolean values, callbacks, array conditions with logical operators,
 * dynamic field evaluation from context data, dependent field visibility,
 * and permission-based field states.
 *
 * The class implements a singleton pattern to ensure consistent
 * conditional evaluation across the application while providing
 * extensible filters and hooks for custom condition handling.
 *
 * @package    UltraguardPro\view
 * @author     Ultraguard Pro Team <support@ultraguardpro.com>
 * @copyright  2024 Ultraguard Pro
 * @license    GPL-2.0-or-later
 * @version    1.2.0
 * @link       https://ultraguardpro.com/docs/conditional-rendering
 */

namespace UltraguardPro\view;

defined( 'ABSPATH' ) || exit;

/**
 * Conditional Renderer
 *
 * Handles advanced conditional evaluation for rendering decisions with
 * support for multiple condition types, logical operators, dynamic
 * context evaluation, dependent field visibility, and permission-based
 * field states. The class provides a flexible framework for
 * evaluating complex boolean logic across user roles, capabilities,
 * WordPress conditionals, custom fields, and nested data structures.
 *
 * This class implements the Singleton pattern to ensure consistent
 * condition evaluation throughout the application. It includes extensive
 * error handling, sanitization, and provides hooks for custom extensions.
 * Conditions can be specified as simple booleans, callbacks, or complex
 * arrays with field/operator/value structures and logical relations.
 *
 * @package    UltraguardPro\view
 * @author     Ultraguard Pro Team
 * @version    1.2.0
 * @since      1.0.0
 * @see        \UltraguardPro\view\RenderEngine
 *
 * @example
 * $renderer = ConditionalRenderer::get_instance();
 *
 * // Dependent field example
 * $field_config = [
 *     'id' => 'advanced_setting',
 *     'depends_on' => [
 *         'relation' => 'AND',
 *         'conditions' => [
 *             ['field' => 'basic_setting', 'operator' => '=', 'value' => 'enabled'],
 *             ['field' => 'user_role', 'operator' => 'in', 'value' => ['admin', 'editor']]
 *         ]
 *     ]
 * ];
 * $should_show = $renderer->should_show_field($field_config, $context);
 *
 * // Permission-based field example
 * $field_config = [
 *     'id' => 'pro_feature',
 *     'permission_condition' => [
 *         'field' => 'user_meta:is_pro',
 *         'operator' => '=',
 *         'value' => 'yes'
 *     ],
 *     'disabled_message' => 'This feature is available for Pro users only.'
 * ];
 * $field_attrs = $renderer->get_field_attributes($field_config, $context);
 */
class ConditionalRenderer {

    /**
     * Singleton instance of ConditionalRenderer
     *
     * Stores the single instance of this class to ensure consistent
     * conditional evaluation throughout the application lifecycle.
     *
     * @var ConditionalRenderer|null
     * @access private
     * @static
     */
    private static $instance = null;

    /**
     * Track resolved dependencies to prevent infinite loops
     *
     * @var array
     * @access private
     */
    private $dependency_chain = [];

    /**
     * Get singleton instance
     *
     * Returns the singleton instance of ConditionalRenderer, creating it
     * if it doesn't exist. This ensures all conditional evaluation uses
     * the same instance and configuration.
     *
     * @return ConditionalRenderer The singleton instance
     * @access public
     * @static
     *
     * @example
     * $renderer = ConditionalRenderer::get_instance();
     */
    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Evaluate condition
     *
     * Main entry point for condition evaluation. Supports multiple condition
     * types: boolean values, callable functions, array conditions, and
     * string-based conditions. Applies context sanitization and provides
     * filter hooks for complete evaluation override.
     *
     * @param  mixed    $condition  Condition to evaluate. Can be bool, callable,
     *                              array, or string.
     * @param  array    $context    Context data for evaluation. Merged with
     *                              default WordPress context values.
     * @return bool                 Evaluation result
     * @access public
     *
     * @example
     * $result = $renderer->evaluate(
     *     ['field' => 'user_role', 'operator' => '=', 'value' => 'admin'],
     *     ['user_id' => 1]
     * );
     */
    public function evaluate( $condition, $context ) {
        // Secure context with defaults and sanitization
        $context = wp_parse_args( $context, [
            'user_id'   => absint( get_current_user_id() ),
            'post_id'   => absint( get_the_ID() ),
            'is_admin'  => (bool) is_admin(),
            'is_ajax'   => (bool) wp_doing_ajax()
        ] );

        // Allow complete evaluation override
        $result = apply_filters( 'ultraguard_condition_evaluate', null, $condition, $context );
        if ( null !== $result ) {
            return (bool) $result;
        }

        if ( empty( $condition ) ) {
            return true;
        }

        if ( is_bool( $condition ) ) {
            return $condition;
        }

        if ( is_callable( $condition ) ) {
            return $this->evaluate_callback( $condition, $context );
        }

        if ( is_array( $condition ) ) {
            return $this->evaluate_array_condition( $condition, $context );
        }

        return (bool) $condition;
    }

    /**
     * Evaluate array-based condition
     *
     * Handles array conditions with support for single field conditions,
     * logical relations (AND/OR), and arrays of boolean values. Validates
     * condition structure and provides detailed error logging in debug mode.
     *
     * @param  array    $condition  Array condition structure
     * @param  array    $context    Context data for evaluation
     * @return bool                 Evaluation result
     * @access protected
     *
     * @see evaluate_single_condition()
     * @see evaluate_logical()
     */
    protected function evaluate_array_condition( $condition, $context ) {
        // Secure condition with defaults
        $condition = wp_parse_args( $condition, [
            'field'       => '',
            'operator'    => '=',
            'value'       => null,
            'relation'    => 'OR',
            'conditions'  => [],
            'callback'    => null
        ] );

        // Single condition
        if ( ! empty( $condition['field'] ) ) {
            return $this->evaluate_single_condition( $condition, $context );
        }

        // Multiple conditions with relation
        if ( ! empty( $condition['relation'] ) && ! empty( $condition['conditions'] ) ) {
            return $this->evaluate_logical( $condition['conditions'], $condition['relation'], $context );
        }


        if ( is_array( $condition ) ) {
            $all_bool = true;
            foreach ( $condition as $value ) {
                if ( ! is_bool( $value ) ) {
                    $all_bool = false;
                    break;
                }
            }

            if ( $all_bool ) {
                // If all values are booleans, use OR logic
                foreach ( $condition as $value ) {
                    if ( $value === true ) {
                        return true;
                    }
                }
                return false;
            }
        }

        // Invalid condition structure
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            \UltraguardPro\core\Logger::error( 'Ultraguard: Invalid condition array structure' );
        }
        return false;
    }

    /**
     * Evaluate single condition
     *
     * Evaluates a single field-based condition with operator comparison.
     * Supports special field types (user_role, user_capability, WordPress
     * conditionals) and custom callback functions.
     *
     * @param  array    $condition  Single condition array with field,
     *                              operator, and value
     * @param  array    $context    Context data for field value resolution
     * @return bool                 Evaluation result
     * @access protected
     *
     * @see get_field_value()
     * @see compare()
     * @see evaluate_role()
     * @see evaluate_capability()
     */
    protected function evaluate_single_condition( $condition, $context ) {
        // Secure condition with defaults
        $condition = wp_parse_args( $condition, [
            'field'     => '',
            'operator'  => '=',
            'value'     => null,
            'callback'  => null
        ] );

        $field          = sanitize_text_field( (string) $condition['field'] );
        $operator       = sanitize_text_field( (string) $condition['operator'] );
        $expected_value = $condition['value'];

        // Get actual value from context
        $actual_value = $this->get_field_value( $field, $context );

        // Special field types
        if ( $field === 'user_role' ) {
            return $this->evaluate_role( $expected_value );
        }

        if ( $field === 'user_capability' ) {
            return $this->evaluate_capability( $expected_value );
        }

        if ( $field === 'is_admin' ) {
            $actual_value = is_admin();
            return $this->compare( $actual_value, $expected_value, $operator );
        }

        if ( $field === 'is_ajax' ) {
            $actual_value = wp_doing_ajax();
            return $this->compare( $actual_value, $expected_value, $operator );
        }

        if ( $field === 'is_user_logged_in' ) {
            $actual_value = is_user_logged_in();
            return $this->compare( $actual_value, $expected_value, $operator );
        }

        // Custom callback
        if ( ! empty( $condition['callback'] ) ) {
            $callback_args = isset( $condition['callback_args'] ) ?
                $condition['callback_args'] :
                [ $actual_value, $expected_value, $context ];
            return $this->evaluate_callback( $condition['callback'], $callback_args );
        }

        return $this->compare( $actual_value, $expected_value, $operator );
    }

    /**
     * Comparison operators
     *
     * Performs comparison between two values using the specified operator.
     * Supports standard comparison operators (=, !=, >, <, etc.), array
     * operators (in, not_in), string operators (contains, starts_with),
     * and special operators (empty, null, isset). Provides filter for
     * custom operator extensions.
     *
     * @param  mixed    $value1    First value for comparison
     * @param  mixed    $value2    Second value for comparison
     * @param  string   $operator  Comparison operator
     * @return bool                Comparison result
     * @access protected
     *
     * @see safe_greater_than()
     * @see safe_in_array()
     * @see safe_contains()
     */
    protected function compare( $value1, $value2, $operator ) {
        // Secure operator with default
        $operator = strtolower( sanitize_text_field( $operator ) );

        switch ( $operator ) {
            case '=':
                return $value1 == $value2;

            case '==':
                return $value1 === $value2;

            case '!=':
            case '<>':
                return $value1 != $value2;

            case '>':
                return $this->safe_greater_than( $value1, $value2 );

            case '>=':
                return $this->safe_greater_than_equal( $value1, $value2 );

            case '<':
                return $this->safe_less_than( $value1, $value2 );

            case '<=':
                return $this->safe_less_than_equal( $value1, $value2 );

            case 'in':
                if ( ! is_array( $value2 ) ) {
                    $value2 = [ $value2 ];
                }
                return $this->safe_in_array( $value1, $value2 );

            case 'not_in':
                if ( ! is_array( $value2 ) ) {
                    $value2 = [ $value2 ];
                }
                return ! $this->safe_in_array( $value1, $value2 );

            case 'contains':
                return $this->safe_contains( $value1, $value2 );

            case 'not_contains':
                return ! $this->safe_contains( $value1, $value2 );

            case 'starts_with':
                return $this->safe_starts_with( $value1, $value2 );

            case 'ends_with':
                return $this->safe_ends_with( $value1, $value2 );

            case 'regex':
                return $this->safe_regex_match( $value1, $value2 );

            case 'empty':
                return empty( $value1 );

            case 'not_empty':
                return ! empty( $value1 );

            case 'null':
                return $value1 === null;

            case 'not_null':
                return $value1 !== null;

            case 'isset':
                return isset( $value1 );

            case 'not_isset':
                return ! isset( $value1 );

            default:
                // Allow custom operators via filter
                return apply_filters( "ultraguard_condition_operator_{$operator}", false, $value1, $value2 );
        }
    }

    /**
     * Safe greater than comparison
     *
     * Performs numeric greater-than comparison with type validation.
     * Returns false if either value is non-numeric to prevent
     * unexpected type coercion issues.
     *
     * @param  mixed  $value1  First value
     * @param  mixed  $value2  Second value
     * @return bool            True if value1 > value2 and both are numeric
     * @access protected
     */
    protected function safe_greater_than( $value1, $value2 ) {
        if ( ! is_numeric( $value1 ) || ! is_numeric( $value2 ) ) {
            return false;
        }
        return floatval( $value1 ) > floatval( $value2 );
    }

    /**
     * Safe greater than or equal comparison
     *
     * Performs numeric greater-than-or-equal comparison with type
     * validation. Returns false if either value is non-numeric.
     *
     * @param  mixed  $value1  First value
     * @param  mixed  $value2  Second value
     * @return bool            True if value1 >= value2 and both are numeric
     * @access protected
     */
    protected function safe_greater_than_equal( $value1, $value2 ) {
        if ( ! is_numeric( $value1 ) || ! is_numeric( $value2 ) ) {
            return false;
        }
        return floatval( $value1 ) >= floatval( $value2 );
    }

    /**
     * Safe less than comparison
     *
     * Performs numeric less-than comparison with type validation.
     * Returns false if either value is non-numeric.
     *
     * @param  mixed  $value1  First value
     * @param  mixed  $value2  Second value
     * @return bool            True if value1 < value2 and both are numeric
     * @access protected
     */
    protected function safe_less_than( $value1, $value2 ) {
        if ( ! is_numeric( $value1 ) || ! is_numeric( $value2 ) ) {
            return false;
        }
        return floatval( $value1 ) < floatval( $value2 );
    }

    /**
     * Safe less than or equal comparison
     *
     * Performs numeric less-than-or-equal comparison with type
     * validation. Returns false if either value is non-numeric.
     *
     * @param  mixed  $value1  First value
     * @param  mixed  $value2  Second value
     * @return bool            True if value1 <= value2 and both are numeric
     * @access protected
     */
    protected function safe_less_than_equal( $value1, $value2 ) {
        if ( ! is_numeric( $value1 ) || ! is_numeric( $value2 ) ) {
            return false;
        }
        return floatval( $value1 ) <= floatval( $value2 );
    }

    /**
     * Safe in_array check
     *
     * Performs strict in_array check with array validation.
     * Returns false if haystack is not an array.
     *
     * @param  mixed  $needle     Value to search for
     * @param  array  $haystack   Array to search in
     * @return bool               True if needle found in haystack
     * @access protected
     */
    protected function safe_in_array( $needle, $haystack ) {
        if ( ! is_array( $haystack ) ) {
            return false;
        }
        return in_array( $needle, $haystack, true );
    }

    /**
     * Safe contains check
     *
     * Checks if value2 is contained within value1. Supports both
     * array containment (checks if value2 is in array) and string
     * containment (substring search). Returns false for invalid types.
     *
     * @param  mixed   $value1  Container (array or string)
     * @param  mixed   $value2  Value to find
     * @return bool             True if value1 contains value2
     * @access protected
     */
    protected function safe_contains( $value1, $value2 ) {
        if ( is_array( $value1 ) ) {
            return in_array( $value2, $value1, true );
        }
        if ( ! is_string( $value1 ) || ! is_string( $value2 ) ) {
            return false;
        }
        return strpos( $value1, $value2 ) !== false;
    }

    /**
     * Safe starts with check
     *
     * Checks if string value1 starts with string value2.
     * Returns false if either value is not a string.
     *
     * @param  string  $value1  String to check
     * @param  string  $value2  Prefix to look for
     * @return bool             True if value1 starts with value2
     * @access protected
     */
    protected function safe_starts_with( $value1, $value2 ) {
        if ( ! is_string( $value1 ) || ! is_string( $value2 ) ) {
            return false;
        }
        return strpos( $value1, $value2 ) === 0;
    }

    /**
     * Safe ends with check
     *
     * Checks if string value1 ends with string value2.
     * Returns true if value2 is empty string. Returns false
     * if either value is not a string.
     *
     * @param  string  $value1  String to check
     * @param  string  $value2  Suffix to look for
     * @return bool             True if value1 ends with value2
     * @access protected
     */
    protected function safe_ends_with( $value1, $value2 ) {
        if ( ! is_string( $value1 ) || ! is_string( $value2 ) ) {
            return false;
        }
        $value2_length = strlen( $value2 );
        if ( 0 === $value2_length ) {
            return true;
        }
        return substr( $value1, -$value2_length ) === $value2;
    }

    /**
     * Safe regex match
     *
     * Performs regular expression match with validation. Checks for
     * valid regex pattern, null bytes, and proper delimiters.
     * Uses error suppression to validate pattern before execution.
     *
     * @param  string  $value1  String to match against
     * @param  string  $value2  Regular expression pattern
     * @return bool             True if pattern matches string
     * @access protected
     */
    protected function safe_regex_match( $value1, $value2 ) {
        if ( ! is_string( $value1 ) || ! is_string( $value2 ) ) {
            return false;
        }

        // Check for valid delimiters and no null bytes
        if ( empty( $value2 ) || strpos( $value2, "\0" ) !== false ) {
            return false;
        }

        // Validate regex pattern
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler -- Temporary local error handler is used only to validate user-provided regex safely.
        set_error_handler( function() { return true; }, E_WARNING );
        $is_valid = @preg_match( $value2, 'test' ) !== false;
        restore_error_handler();

        if ( ! $is_valid ) {
            return false;
        }

        return preg_match( $value2, $value1 ) === 1;
    }

    /**
     * Logical operators (AND/OR)
     *
     * Evaluates multiple conditions with logical relation (AND/OR).
     * Each condition is evaluated individually and results are combined
     * according to the specified logical operator.
     *
     * @param  array   $conditions  Array of conditions to evaluate
     * @param  string  $relation    Logical relation ('AND' or 'OR')
     * @param  array   $context     Context data for condition evaluation
     * @return bool                 Combined logical result
     * @access protected
     *
     * @see evaluate()
     */
    protected function evaluate_logical( $conditions, $relation, $context ) {
        if ( ! is_array( $conditions ) || empty( $conditions ) ) {
            return true;
        }

        $relation = strtoupper( sanitize_text_field( $relation ) );
        $results  = [];

        foreach ( $conditions as $condition ) {
            if ( ! is_array( $condition ) ) {
                $results[] = false;
                continue;
            }
            $results[] = $this->evaluate( $condition, $context );
        }

        if ( 'AND' === $relation ) {
            foreach ( $results as $result ) {
                if ( ! $result ) {
                    return false;
                }
            }
            return true;
        } elseif ( 'OR' === $relation ) {
            foreach ( $results as $result ) {
                if ( $result ) {
                    return true;
                }
            }
            return false;
        }

        return false;
    }

    /**
     * Role-based conditions
     *
     * Evaluates if current user has any of the specified roles.
     * Supports single role string or array of roles. Returns false
     * for non-existent users or empty role specifications.
     *
     * @param  string|string[]  $roles  Role or array of roles to check
     * @return bool                     True if user has any specified role
     * @access protected
     */
    protected function evaluate_role( $roles ) {
        $user = wp_get_current_user();

        if ( ! $user->exists() ) {
            return false;
        }

        if ( empty( $roles ) ) {
            return false;
        }

        if ( ! is_array( $roles ) ) {
            $roles = [ $roles ];
        }

        // Sanitize role names
        $roles = array_map( 'sanitize_text_field', array_filter( $roles, 'is_string' ) );

        $user_roles = (array) $user->roles;

        foreach ( $roles as $role ) {
            if ( in_array( $role, $user_roles, true ) ) {
                return true;
            }
        }

        return false;
    }

    /**
     * Capability-based conditions
     *
     * Evaluates if current user has the specified capability.
     * Returns false for empty or non-string capability values.
     * Uses WordPress core current_user_can() function.
     *
     * @param  string  $capability  Capability to check
     * @return bool                 True if user has capability
     * @access protected
     */
    protected function evaluate_capability( $capability ) {
        if ( empty( $capability ) || ! is_string( $capability ) ) {
            return false;
        }

        $capability = sanitize_text_field( $capability );
        return 'manage_options' === $capability
            ? \UltraguardPro\ultraguard_current_user_can_manage()
            : current_user_can( $capability );
    }

    /**
     * Custom condition callbacks
     *
     * Evaluates a custom callback function with provided arguments.
     * Wraps execution in try-catch block for error handling and
     * provides before/after action hooks for integration.
     *
     * @param  callable  $callback  Callback function to execute
     * @param  array     $args      Arguments to pass to callback
     * @return bool                 Callback execution result
     * @access protected
     *
     * @throws \Exception  Re-throws any exceptions caught during execution
     */
    protected function evaluate_callback( $callback, $args ) {
        if ( ! is_callable( $callback ) ) {
            return false;
        }

        try {
            do_action( 'ultraguard_before_condition_callback', $callback, $args );
            $result = (bool) call_user_func_array( $callback, $args );
            do_action( 'ultraguard_after_condition_callback', $callback, $args, $result );
            return $result;
        } catch ( \Exception $e ) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                \UltraguardPro\core\Logger::error(
                    sprintf(
                        /* translators: Placeholder values are inserted into this string. */
                        __( 'ConditionalRenderer callback error: %s', 'ultraguard-security-suite'),
                        $e->getMessage()
                    )
                );
            }
            return false;
        }
    }

    /**
     * Get field value from context
     *
     * Resolves field values from context using multiple strategies:
     * direct context access, dot notation for nested values, WordPress
     * conditionals, option values, user/post meta, and custom filters.
     *
     * @param  string  $field    Field name/path to resolve
     * @param  array   $context  Context data for resolution
     * @return mixed             Resolved field value or null
     * @access protected
     *
     * @see get_nested_field_value()
     */
    protected function get_field_value( $field, $context ) {
        if ( empty( $field ) || ! is_string( $field ) ) {
            return null;
        }

        $field = sanitize_text_field( $field );

        // Direct context value
        if ( isset( $context[ $field ] ) ) {
            return $context[ $field ];
        }

        // Nested field (dot notation)
        if ( strpos( $field, '.' ) !== false ) {
            return $this->get_nested_field_value( $field, $context );
        }

        // WordPress conditionals
        if ( strpos( $field, 'is_' ) === 0 ) {
            $allowed_conditionals = [
                'is_admin', 'is_ajax', 'is_blog_admin', 'is_category', 'is_comment_feed',
                'is_customize_preview', 'is_date', 'is_day', 'is_feed', 'is_front_page',
                'is_home', 'is_month', 'is_page', 'is_page_template', 'is_paged',
                'is_post_type_archive', 'is_preview', 'is_robots', 'is_rtl', 'is_search',
                'is_single', 'is_singular', 'is_ssl', 'is_tag', 'is_tax', 'is_time',
                'is_trackback', 'is_user_logged_in', 'is_year'
            ];

            if ( in_array( $field, $allowed_conditionals, true ) && function_exists( $field ) ) {
                return call_user_func( $field );
            }
        }

        // Option value
        if ( strpos( $field, 'option:' ) === 0 ) {
            $option_name = substr( $field, 7 );
            return $option_name ? get_option( sanitize_text_field( $option_name ) ) : null;
        }

        // User meta
        if ( strpos( $field, 'user_meta:' ) === 0 ) {
            $meta_key = substr( $field, 10 );
            $user_id = get_current_user_id();
            return $meta_key && $user_id ? get_user_meta( $user_id, sanitize_text_field( $meta_key ), true ) : null;
        }

        // Post meta
        if ( strpos( $field, 'post_meta:' ) === 0 ) {
            $meta_key = substr( $field, 10 );
            $post_id = get_the_ID();
            return $meta_key && $post_id ? get_post_meta( $post_id, sanitize_text_field( $meta_key ), true ) : null;
        }

        // Custom field via filter
        return apply_filters( "ultraguard_condition_field_{$field}", null, $context );
    }

    /**
     * Get nested field value using dot notation
     *
     * Resolves nested field values from context using dot notation
     * (e.g., 'user.profile.name'). Returns null if any level in
     * the path doesn't exist or isn't an array.
     *
     * @param  string  $field    Dot-notation field path
     * @param  array   $context  Context data to traverse
     * @return mixed             Nested field value or null
     * @access protected
     */
    protected function get_nested_field_value( $field, $context ) {
        $keys  = explode( '.', $field );
        $value = $context;

        foreach ( $keys as $key ) {
            $key = sanitize_text_field( $key );
            if ( ! is_array( $value ) || ! isset( $value[ $key ] ) ) {
                return null;
            }
            $value = $value[ $key ];
        }

        return $value;
    }

    /**
     * Validate condition structure
     *
     * Validates condition structure for correctness before evaluation.
     * Supports validation of all condition types and provides filter
     * for custom validation logic. Useful for configuration validation
     * and debugging.
     *
     * @param  mixed  $condition  Condition structure to validate
     * @return bool               True if condition structure is valid
     * @access public
     *
     * @example
     * $is_valid = $renderer->validate_condition([
     *     'field' => 'user_role',
     *     'operator' => 'in',
     *     'value' => ['admin', 'editor']
     * ]);
     */
    public function validate_condition( $condition ) {
        // Allow custom validation
        $valid = apply_filters( 'ultraguard_condition_validate', null, $condition );
        if ( null !== $valid ) {
            return $valid;
        }

        if ( empty( $condition ) ) {
            return true;
        }

        if ( is_bool( $condition ) || is_callable( $condition ) ) {
            return true;
        }
        if ( is_string( $condition ) ) {
            // Validate string conditions (could be "true", "false", or field path)
            $valid_strings = [ 'true', 'false', '1', '0', 'yes', 'no' ];
            return in_array( strtolower( $condition ), $valid_strings, true ) ||
                   preg_match( '/^[a-zA-Z0-9_.:]+$/', $condition );
        }

        if ( is_array( $condition ) ) {
            // Check for single condition
            if ( isset( $condition['field'] ) ) {
                return ! empty( $condition['field'] );
            }

            // Check for logical condition
            if ( isset( $condition['relation'] ) ) {
                if ( ! in_array( strtoupper( $condition['relation'] ), [ 'AND', 'OR' ] ) ) {
                    return false;
                }
                if ( empty( $condition['conditions'] ) || ! is_array( $condition['conditions'] ) ) {
                    return false;
                }

                // Validate nested conditions
                foreach ( $condition['conditions'] as $nested_condition ) {
                    if ( ! $this->validate_condition( $nested_condition ) ) {
                        return false;
                    }
                }
                return true;
            }

            // Array of conditions
            foreach ( $condition as $nested_condition ) {
                if ( ! $this->validate_condition( $nested_condition ) ) {
                    return false;
                }
            }
            return true;
        }

        return false;
    }

    /**
     * Determine if a field should be shown based on dependencies
     *
     * Evaluates dependent field visibility logic. A field is shown only if
     * all its dependency conditions are met. Supports nested dependencies
     * and prevents infinite dependency loops.
     *
     * @param  array  $field_config  Field configuration array
     * @param  array  $context       Context data for evaluation
     * @return bool                  True if field should be shown
     * @access public
     *
     * @example
     * $field_config = [
     *     'id' => 'advanced_setting',
     *     'depends_on' => [
     *         'relation' => 'AND',
     *         'conditions' => [
     *             ['field' => 'basic_setting', 'operator' => '=', 'value' => 'enabled'],
     *             ['field' => 'user_role', 'operator' => 'in', 'value' => ['admin', 'editor']]
     *         ]
     *     ]
     * ];
     * $should_show = $renderer->should_show_field($field_config, $context);
     */
    public function should_show_field( $field_config, $context ) {
        // Apply before visibility check hook
        do_action( 'ultraguard_before_field_visibility_check', $field_config, $context );

        // Check for custom visibility override
        $visibility = apply_filters( 'ultraguard_field_visibility', null, $field_config, $context );
        if ( null !== $visibility ) {
            return (bool) $visibility;
        }

        // Secure field config
        $field_config = wp_parse_args( $field_config, [
            'id'         => '',
            'depends_on' => null,
            'conditions' => null // Backward compatibility
        ] );

        $field_id = sanitize_text_field( $field_config['id'] );

        // Check for dependency loops
        if ( in_array( $field_id, $this->dependency_chain, true ) ) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                \UltraguardPro\core\Logger::error(
                    sprintf(
                        /* translators: Placeholder values are inserted into this string. */
                        __( 'Ultraguard: Dependency loop detected for field "%1$s". Chain: %2$s', 'ultraguard-security-suite'),
                        $field_id,
                        implode( ' -> ', $this->dependency_chain )
                    )
                );
            }
            return false;
        }

        // Add current field to dependency chain
        $this->dependency_chain[] = $field_id;

        $should_show = true;

        // Check dependencies (depends_on takes precedence over conditions for backward compatibility)
        $dependency = ! empty( $field_config['depends_on'] ) ? $field_config['depends_on'] : $field_config['conditions'];

        if ( ! empty( $dependency ) ) {
            if ( is_array( $dependency ) && isset( $dependency['conditions'] ) ) {
                // Complex dependency with relation
                $should_show = $this->evaluate_logical(
                    $dependency['conditions'],
                    $dependency['relation'] ?? 'AND',
                    $context
                );
            } else {
                // Simple condition or array of conditions
                $should_show = $this->evaluate( $dependency, $context );
            }
        }

        // Remove current field from dependency chain
        array_pop( $this->dependency_chain );

        // Apply after visibility check hook
        do_action( 'ultraguard_after_field_visibility_check', $field_config, $context, $should_show );

        return $should_show;
    }

    /**
     * Get field state based on permissions
     *
     * Determines if a field should be enabled or disabled based on
     * permission conditions. Returns an array with state information
     * including custom message to display.
     *
     * @param  array  $field_config  Field configuration array
     * @param  array  $context       Context data for evaluation
     * @return array                 Field state array
     * @access public
     *
     * @example
     * $field_config = [
     *     'id' => 'pro_feature',
     *     'permission_condition' => [
     *         'field' => 'user_meta:is_pro',
     *         'operator' => '=',
     *         'value' => 'yes'
     *     ],
     *     'disabled_message' => 'This feature is available for Pro users only.'
     * ];
     * $state = $renderer->get_field_state($field_config, $context);
     * // Returns: ['enabled' => false, 'message' => 'This feature...', 'reason' => 'not_pro']
     */
    public function get_field_state( $field_config, $context ) {
        // Secure field config
        $field_config = wp_parse_args( $field_config, [
            'id'                   => '',
            'permission_condition' => null,
            'disabled_message'     => '',
            'disabled_reason'      => 'insufficient_permission'
        ] );

        $default_state = [
            'enabled' => true,
            'message' => '',
            'reason'  => '',
            'class'   => ''
        ];

        // Apply filter for custom state logic
        $state = apply_filters( 'ultraguard_field_state', null, $field_config, $context, $default_state );
        if ( null !== $state && is_array( $state ) ) {
            return wp_parse_args( $state, $default_state );
        }

        $state = $default_state;

        // Check permission condition
        if ( ! empty( $field_config['permission_condition'] ) ) {
            $has_permission = $this->evaluate( $field_config['permission_condition'], $context );

            if ( ! $has_permission ) {
                $state['enabled'] = false;
                $state['reason']  = sanitize_text_field( $field_config['disabled_reason'] );

                // Add custom message if provided
                if ( ! empty( $field_config['disabled_message'] ) ) {
                    $state['message'] = wp_kses_post( is_array( $field_config['disabled_message'] ) ? esc_html( wp_json_encode( $field_config['disabled_message'] ) ) : (string)( $field_config['disabled_message'] ) );
                }

                // Add CSS class for styling
                $state['class'] = 'ultraguard-disabled-field';
            }
        }

        return $state;
    }

    /**
     * Get HTML attributes for a field based on its state
     *
     * Generates HTML attributes string for form fields based on
     * field state. Includes disabled attribute, CSS classes, and
     * data attributes for JavaScript handling.
     *
     * @param  array  $field_config  Field configuration array
     * @param  array  $context       Context data for evaluation
     * @return string                HTML attributes string
     * @access public
     *
     * @example
     * $attrs = $renderer->get_field_attributes($field_config, $context);
     * // Returns: 'disabled="disabled" class="ultraguard-disabled-field" data-message="Upgrade required"'
     */
    public function get_field_attributes( $field_config, $context ) {
        $state = $this->get_field_state( $field_config, $context );

        $attributes = [];

        if ( ! $state['enabled'] ) {
            $attributes['disabled'] = 'disabled';

            if ( ! empty( $state['class'] ) ) {
                $attributes['class'] = $state['class'];
            }

            if ( ! empty( $state['message'] ) ) {
                $attributes['data-message'] = esc_attr( $state['message'] );
            }

            if ( ! empty( $state['reason'] ) ) {
                $attributes['data-reason'] = esc_attr( $state['reason'] );
            }

            $attributes['data-field-id'] = esc_attr( $field_config['id'] ?? '' );
            $attributes['aria-disabled'] = 'true';

            if ( ! empty( $state['message'] ) ) {
                $attributes['aria-describedby'] = 'ultraguard-message-' . sanitize_html_class( $field_config['id'] ?? '' );
            }
        }

        // Build attributes string
        $attr_string = '';
        foreach ( $attributes as $key => $value ) {
            $attr_string .= ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
        }

        return trim( $attr_string );
    }

    /**
     * Render field with conditional logic applied
     *
     * Complete field rendering method that handles both visibility
     * and state logic. Returns either the rendered field HTML or
     * empty string if field should not be shown.
     *
     * @param  array   $field_config  Field configuration array
     * @param  array   $context       Context data for evaluation
     * @param  string  $field_html    Raw field HTML to conditionally render
     * @return string                 Conditionally rendered field HTML
     * @access public
     *
     * @example
     * $html = $renderer->render_field([
     *     'id' => 'pro_feature',
     *     'depends_on' => ['field' => 'basic_setting', 'operator' => '=', 'value' => 'enabled'],
     *     'permission_condition' => ['field' => 'user_meta:is_pro', 'operator' => '=', 'value' => 'yes'],
     *     'disabled_message' => 'Pro feature'
     * ], $context, '<input type="text" name="pro_feature" />');
     */
    public function render_field( $field_config, $context, $field_html ) {
        // Check if field should be shown
        if ( ! $this->should_show_field( $field_config, $context ) ) {
            return '';
        }

        // Get field state and attributes
        $state = $this->get_field_state( $field_config, $context );
        $attributes = $this->get_field_attributes( $field_config, $context );

        // Apply attributes to field HTML
        $rendered_html = $field_html;

        if ( ! empty( $attributes ) ) {
            // Simple attribute injection for common input types
            if ( preg_match( '/<(input|select|textarea|button)/i', $field_html ) ) {
                // Add attributes before closing > of opening tag
                $rendered_html = preg_replace(
                    '/<(input|select|textarea|button)([^>]*?)(\/?)>/i',
                    '<$1$2 ' . $attributes . '$3>',
                    $field_html,
                    1
                );
            }
        }

        // Add message container if needed
        if ( ! $state['enabled'] && ! empty( $state['message'] ) ) {
            $message_id = 'ultraguard-message-' . sanitize_html_class( $field_config['id'] ?? '' );
            $rendered_html .= sprintf(
                '<div id="%s" class="ultraguard-field-message ultraguard-field-message-%s">%s</div>',
                esc_attr( $message_id ),
                esc_attr( $state['reason'] ),
                wp_kses_post( is_array( $state['message'] ) ? esc_html( wp_json_encode( $state['message'] ) ) : (string)( $state['message'] ) )
            );
        }

        // Wrap field if disabled for additional styling
        if ( ! $state['enabled'] ) {
            $rendered_html = sprintf(
                '<div class="ultraguard-field-wrapper ultraguard-field-disabled" data-field-id="%s">%s</div>',
                esc_attr( $field_config['id'] ?? '' ),
                $rendered_html
            );
        }

        return $rendered_html;
    }

    /**
     * Validate field configuration
     *
     * Validates field configuration structure for dependent fields
     * and permission-based fields. Checks for required fields,
     * valid condition structures, and dependency loops.
     *
     * @param  array  $field_config  Field configuration to validate
     * @return array                 Validation results with errors/warnings
     * @access public
     *
     * @example
     * $validation = $renderer->validate_field_config($field_config);
     * if (!$validation['valid']) {
     *     foreach ($validation['errors'] as $error) {
     *         error_log($error);
     *     }
     * }
     */
    public function validate_field_config( $field_config ) {
        $result = [
            'valid'    => true,
            'errors'   => [],
            'warnings' => []
        ];

        if ( ! is_array( $field_config ) ) {
            $result['valid'] = false;
            $result['errors'][] = __( 'Field configuration must be an array', 'ultraguard-security-suite');
            return $result;
        }

        // Check for field ID
        if ( empty( $field_config['id'] ) || ! is_string( $field_config['id'] ) ) {
            $result['valid'] = false;
            $result['errors'][] = __( 'Field configuration must have a valid "id" string', 'ultraguard-security-suite');
        }

        // Validate depends_on if present
        if ( isset( $field_config['depends_on'] ) && ! empty( $field_config['depends_on'] ) ) {
            if ( ! $this->validate_condition( $field_config['depends_on'] ) ) {
                $result['valid'] = false;
                $result['errors'][] = sprintf(
                    /* translators: Placeholder values are inserted into this string. */
                    __( 'Invalid "depends_on" condition for field "%s"', 'ultraguard-security-suite'),
                    $field_config['id']
                );
            }
        }

        // Validate permission_condition if present
        if ( isset( $field_config['permission_condition'] ) && ! empty( $field_config['permission_condition'] ) ) {
            if ( ! $this->validate_condition( $field_config['permission_condition'] ) ) {
                $result['valid'] = false;
                $result['errors'][] = sprintf(
                    /* translators: Placeholder values are inserted into this string. */
                    __( 'Invalid "permission_condition" for field "%s"', 'ultraguard-security-suite'),
                    $field_config['id']
                );
            }
        }

        // Check for potential dependency loops
        if ( isset( $field_config['depends_on'] ) && is_array( $field_config['depends_on'] ) ) {
            if ( $this->has_dependency_loop( $field_config['id'], $field_config['depends_on'] ) ) {
                $result['warnings'][] = sprintf(
                    /* translators: Placeholder values are inserted into this string. */
                    __( 'Potential dependency loop detected for field "%s"', 'ultraguard-security-suite'),
                    $field_config['id']
                );
            }
        }

        return $result;
    }

    /**
     * Check for dependency loops in condition
     *
     * Recursively checks if a condition references the original field,
     * which would create a dependency loop.
     *
     * @param  string  $field_id     Original field ID
     * @param  mixed   $condition    Condition to check
     * @param  array   $checked_ids  Already checked field IDs (for recursion)
     * @return bool                  True if loop detected
     * @access protected
     */
    protected function has_dependency_loop( $field_id, $condition, $checked_ids = [] ) {
        $checked_ids[] = $field_id;

        if ( is_array( $condition ) ) {
            // Check single condition field
            if ( isset( $condition['field'] ) ) {
                $dep_field = $condition['field'];
                if ( $dep_field === $field_id ) {
                    return true;
                }

                // Check nested dependencies
                if ( ! in_array( $dep_field, $checked_ids ) ) {
                    // This would require checking other field configs
                    // In a complete implementation, you'd look up the referenced field's config
                }
            }

            // Check conditions array
            if ( isset( $condition['conditions'] ) && is_array( $condition['conditions'] ) ) {
                foreach ( $condition['conditions'] as $sub_condition ) {
                    if ( $this->has_dependency_loop( $field_id, $sub_condition, $checked_ids ) ) {
                        return true;
                    }
                }
            }

            // Check array of conditions
            foreach ( $condition as $value ) {
                if ( is_array( $value ) && $this->has_dependency_loop( $field_id, $value, $checked_ids ) ) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Get all dependent fields for a given field
     *
     * Returns an array of field IDs that depend on the specified field.
     * Useful for clearing cache or updating dependent fields when a field changes.
     *
     * @param  string  $field_id       Field ID to check dependencies for
     * @param  array   $all_fields     Array of all field configurations
     * @return array                   Array of dependent field IDs
     * @access public
     *
     * @example
     * $dependent_fields = $renderer->get_dependent_fields('basic_setting', $all_field_configs);
     */
    public function get_dependent_fields( $field_id, $all_fields ) {
        $dependent_fields = [];

        foreach ( $all_fields as $config ) {
            if ( empty( $config['id'] ) || ! is_string( $config['id'] ) ) {
                continue;
            }

            if ( $this->field_depends_on( $config, $field_id ) ) {
                $dependent_fields[] = $config['id'];

                // Recursively get dependencies of dependencies
                $nested_dependents = $this->get_dependent_fields( $config['id'], $all_fields );
                $dependent_fields = array_merge( $dependent_fields, $nested_dependents );
            }
        }

        return array_unique( $dependent_fields );
    }

    /**
     * Check if a field depends on another field
     *
     * Determines if the given field configuration has a dependency
     * on the specified field ID.
     *
     * @param  array   $field_config   Field configuration
     * @param  string  $target_field   Field ID to check dependency on
     * @return bool                    True if field depends on target
     * @access protected
     */
    protected function field_depends_on( $field_config, $target_field ) {
        $dependencies = [];

        // Extract dependencies from depends_on
        if ( isset( $field_config['depends_on'] ) ) {
            $dependencies = $this->extract_field_dependencies( $field_config['depends_on'] );
        }

        // Check for target field in dependencies
        return in_array( $target_field, $dependencies, true );
    }

    /**
     * Extract field dependencies from a condition
     *
     * Recursively extracts all field references from a condition structure.
     *
     * @param  mixed   $condition    Condition to analyze
     * @param  array   $dependencies Accumulated dependencies (for recursion)
     * @return array                 Array of field IDs referenced in condition
     * @access protected
     */
    protected function extract_field_dependencies( $condition, $dependencies = [] ) {
        if ( is_array( $condition ) ) {
            // Single condition with field
            if ( isset( $condition['field'] ) && is_string( $condition['field'] ) ) {
                $field = $condition['field'];

                // Check if it's a direct field reference (not a WordPress conditional or meta field)
                if ( ! preg_match( '/^(is_|option:|user_meta:|post_meta:)/', $field ) ) {
                    $dependencies[] = $field;
                }
            }

            // Conditions array
            if ( isset( $condition['conditions'] ) && is_array( $condition['conditions'] ) ) {
                foreach ( $condition['conditions'] as $sub_condition ) {
                    $dependencies = $this->extract_field_dependencies( $sub_condition, $dependencies );
                }
            }

            // Array of conditions
            foreach ( $condition as $value ) {
                if ( is_array( $value ) ) {
                    $dependencies = $this->extract_field_dependencies( $value, $dependencies );
                }
            }
        }

        return array_unique( $dependencies );
    }

    /**
     * Clear dependency chain (useful for testing)
     *
     * Clears the internal dependency chain tracking. Useful when
     * performing multiple independent evaluations in sequence.
     *
     * @return void
     * @access public
     */
    public function clear_dependency_chain() {
        $this->dependency_chain = [];
    }
}
