<?php

namespace GriffinForms\Admin\App\Html\Modals\ConditionalLogic;

trait RuleSummaries
{
    protected $field_label_cache = [];
    protected $field_type_cache = [];
    protected $field_missing_cache = [];
    protected $date_display_format = null;
    protected $row_label_cache = [];

    // Builds a human-readable title for the given rule index and configuration.
    protected function getRuleTitle($index, array $rule_data = [])
    {
        $rule_number = (int) $index + 1;
        $fallback_title = sprintf(__('Rule %d', 'griffinforms-form-builder'), $rule_number);

        if (empty($rule_data) || !is_array($rule_data)) {
            return $fallback_title;
        }

        $primary_condition = $this->getPrimaryConditionSummary($rule_data, 80);
        $condition_count = $this->countRuleConditions($rule_data);
        $primary_action = $this->getPrimaryActionSummary($rule_data, 80);
        $action_count = $this->countRuleActions($rule_data);

        if ('' === $primary_condition && '' === $primary_action && $action_count < 1 && $condition_count < 1) {
            return $fallback_title;
        }

        $segments = [];

        $has_condition = '' !== $primary_condition;
        if ($has_condition) {
            $condition_text = sprintf(__('If %s', 'griffinforms-form-builder'), $primary_condition);
            if ($condition_count > 1) {
                $remaining_conditions = $condition_count - 1;
                $additional_conditions_label = sprintf(
                    _n('(+ %d more condition)', '(+ %d more conditions)', $remaining_conditions, 'griffinforms-form-builder'),
                    $remaining_conditions
                );
                $condition_text .= ' ' . sprintf(
                    '<span class="griffinforms-logic-summary-meta text-secondary fst-italic">%s</span>',
                    esc_html($additional_conditions_label)
                );
            }
            $segments[] = $condition_text;
        }

        if ('' !== $primary_action) {
            $action_descriptor = $primary_action;
            if ($action_count > 1) {
                $remaining_actions = $action_count - 1;
                $additional_actions_label = sprintf(
                    _n('(+ %d more action)', '(+ %d more actions)', $remaining_actions, 'griffinforms-form-builder'),
                    $remaining_actions
                );
                $action_descriptor .= ' ' . sprintf(
                    '<span class="griffinforms-logic-summary-meta text-secondary fst-italic">%s</span>',
                    esc_html($additional_actions_label)
                );
            }

            if ($has_condition) {
                $segments[] = sprintf(__('then %s', 'griffinforms-form-builder'), $action_descriptor);
            } else {
                $segments[] = $action_descriptor;
            }
        } elseif ($action_count < 1) {
            $do_nothing = __('do nothing', 'griffinforms-form-builder');
            if ($has_condition) {
                $segments[] = sprintf(__('then %s', 'griffinforms-form-builder'), $do_nothing);
            } else {
                $segments[] = $do_nothing;
            }
        }

        if (empty($segments)) {
            return $fallback_title;
        }

        return implode(' ', $segments);
    }

    // Counts how many individual conditions exist within a rule definition.
    protected function countRuleConditions(array $rule_data)
    {
        $count = 0;
        foreach (['all', 'any'] as $group_key) {
            if (!isset($rule_data[$group_key]) || !is_array($rule_data[$group_key])) {
                continue;
            }
            foreach ($rule_data[$group_key] as $entry) {
                if (is_array($entry) && !empty($entry)) {
                    $count++;
                }
            }
        }

        return $count;
    }

    // Counts how many actions are configured for a rule.
    protected function countRuleActions(array $rule_data)
    {
        if (!isset($rule_data['actions']) || !is_array($rule_data['actions'])) {
            return 0;
        }

        $count = 0;
        foreach ($rule_data['actions'] as $entry) {
            if (is_array($entry) && !empty($entry)) {
                $count++;
            }
        }

        return $count;
    }

    // Retrieves the first non-empty condition summary with an optional length limit.
    protected function getPrimaryConditionSummary(array $rule_data, $limit = 120)
    {
        foreach (['all', 'any'] as $group_key) {
            if (!isset($rule_data[$group_key]) || !is_array($rule_data[$group_key])) {
                continue;
            }

            foreach ($rule_data[$group_key] as $entry) {
                if (!is_array($entry)) {
                    continue;
                }

                $summary = $this->formatConditionSnippet($entry, $limit);
                if ('' !== $summary) {
                    return $summary;
                }
            }
        }

        return '';
    }

    // Retrieves the first non-empty action summary with an optional length limit.
    protected function getPrimaryActionSummary(array $rule_data, $limit = 120)
    {
        if (!isset($rule_data['actions']) || !is_array($rule_data['actions'])) {
            return '';
        }

        foreach ($rule_data['actions'] as $entry) {
            if (!is_array($entry)) {
                continue;
            }

            // Render as an inline clause (lowercase leading verb)
            $summary = $this->formatActionSnippet($entry, $limit, true);
            if ('' !== $summary) {
                return $summary;
            }
        }

        return '';
    }

    // Converts a condition array into a truncated HTML snippet for display.
    protected function formatConditionSnippet(array $condition, $limit = 120)
    {
        $item_type = isset($condition['item']) ? (string) $condition['item'] : '';
        $field_id_raw = isset($condition['item_id']) ? $condition['item_id'] : '';
        $field_id = is_numeric($field_id_raw) ? (int) $field_id_raw : 0;

        $field_label = '';
        $field_type = '';
        if ('field' === $item_type && $field_id > 0) {
            $field_label = $this->getFieldLabelById($field_id);
            $field_type = $this->getFieldTypeById($field_id);
        } elseif ('row' === $item_type && $field_id > 0) {
            // Prefer the DB "name" for rows in summaries
            $field_label = $this->getRowNameById($field_id);
        }

        $is_address_field = ('field' === $item_type && 'inputaddress' === strtolower((string) $field_type));
        $address_condition_map = [];

        if ($is_address_field) {
            $address_condition_map = [
                'line1_equals'       => ['component' => __('address line 1 value', 'griffinforms-form-builder'), 'verb' => __('is', 'griffinforms-form-builder'), 'requires_input' => true],
                'line1_contains'     => ['component' => __('address line 1 value', 'griffinforms-form-builder'), 'verb' => __('contains', 'griffinforms-form-builder'), 'requires_input' => true],
                'line2_contains'     => ['component' => __('address line 2 value', 'griffinforms-form-builder'), 'verb' => __('contains', 'griffinforms-form-builder'), 'requires_input' => true],
                'city_equals'        => ['component' => __('city value', 'griffinforms-form-builder'), 'verb' => __('is', 'griffinforms-form-builder'), 'requires_input' => true],
                'city_contains'      => ['component' => __('city value', 'griffinforms-form-builder'), 'verb' => __('contains', 'griffinforms-form-builder'), 'requires_input' => true],
                'state_equals'       => ['component' => __('state / province value', 'griffinforms-form-builder'), 'verb' => __('is', 'griffinforms-form-builder'), 'requires_input' => true],
                'state_in_list'      => ['component' => __('state / province value', 'griffinforms-form-builder'), 'verb' => __('is one of', 'griffinforms-form-builder'), 'requires_input' => true],
                'postal_equals'      => ['component' => __('postal / ZIP code value', 'griffinforms-form-builder'), 'verb' => __('is', 'griffinforms-form-builder'), 'requires_input' => true],
                'postal_starts_with' => ['component' => __('postal / ZIP code value', 'griffinforms-form-builder'), 'verb' => __('starts with', 'griffinforms-form-builder'), 'requires_input' => true],
                'country_equals'     => ['component' => __('country value', 'griffinforms-form-builder'), 'verb' => __('is', 'griffinforms-form-builder'), 'requires_input' => true],
                'country_in_list'    => ['component' => __('country value', 'griffinforms-form-builder'), 'verb' => __('is one of', 'griffinforms-form-builder'), 'requires_input' => true],
                'country_not_in_list'=> ['component' => __('country value', 'griffinforms-form-builder'), 'verb' => __('is none of', 'griffinforms-form-builder'), 'requires_input' => true],
                'address_is_complete'=> ['component' => __('address', 'griffinforms-form-builder'), 'verb' => __('is complete', 'griffinforms-form-builder'), 'requires_input' => false],
                'address_is_not_complete' => ['component' => __('address', 'griffinforms-form-builder'), 'verb' => __('is not complete', 'griffinforms-form-builder'), 'requires_input' => false],
            ];
        }

        if ('' === $field_label) {
            $field_label = $this->getChoiceLabel('ifItems', $item_type);
        }

        $field_label = trim((string) $field_label);
        if ('' === $field_label) {
            $field_label = __('Field', 'griffinforms-form-builder');
        }

        $attribute_key = isset($condition['attribute']) ? (string) $condition['attribute'] : '';
        $attribute_key_lower = strtolower($attribute_key);
        $attribute_label = '';
        if ('' !== $attribute_key) {
            if ('user' === $item_type) {
                $attribute_label = $this->getChoiceLabel('userAttrs', $attribute_key);
            } else {
                $attribute_label = $this->getChoiceLabel('fieldAttrs', $attribute_key);
            }
        }
        $attribute_label = trim((string) $attribute_label);

        $evaluator_key = isset($condition['evaluator']) ? (string) $condition['evaluator'] : '';
        $evaluator_label = '';
        if ('' !== $evaluator_key && !$is_address_field) {
            $evaluator_label = $this->getEvaluatorLabel($evaluator_key);
        }
        $evaluator_label = trim((string) $evaluator_label);

        $input_value = isset($condition['input']) ? trim((string) $condition['input']) : '';

        // Payment-specific display tweaks.
        if ('field' === $item_type && 'payment' === strtolower((string) $field_type)) {
            if (in_array($attribute_key_lower, ['selected_product'], true) && '' !== $input_value) {
                $input_value = $this->resolvePaymentProductLabel($field_id, $input_value);
            } elseif (in_array($attribute_key_lower, ['selected_gateway'], true) && '' !== $input_value) {
                $input_value = $this->resolvePaymentGatewayLabel($input_value);
            }

            // Build a friendlier summary for payment attributes.
            $payment_summary = '';
            $label_prefix = trim($field_label) !== '' ? trim($field_label) . ': ' : '';
            switch ($attribute_key_lower) {
                case 'selected_product':
                    if ('' !== $input_value) {
                        if (in_array($evaluator_key_lower, ['contains'], true)) {
                            $payment_summary = sprintf(__('selected products include %s', 'griffinforms-form-builder'), $input_value);
                        } elseif (in_array($evaluator_key_lower, ['not_contains', 'does_not_contain'], true)) {
                            $payment_summary = sprintf(__('selected products do not include %s', 'griffinforms-form-builder'), $input_value);
                        } elseif (in_array($evaluator_key_lower, ['not_equal_to'], true)) {
                            $payment_summary = sprintf(__('selected product is not %s', 'griffinforms-form-builder'), $input_value);
                        } else {
                            $payment_summary = sprintf(__('selected product is %s', 'griffinforms-form-builder'), $input_value);
                        }
                    }
                    break;
                case 'product_quantity':
                    if ('' !== $input_value) {
                        $verb = $this->getEvaluatorLabel($evaluator_key) ?: $evaluator_label;
                        $payment_summary = sprintf(__('product quantity %s %s', 'griffinforms-form-builder'), strtolower(trim($verb)), $input_value);
                    }
                    break;
                case 'total_amount':
                    if ('' !== $input_value) {
                        $verb = $this->getEvaluatorLabel($evaluator_key) ?: $evaluator_label;
                        $payment_summary = sprintf(__('total amount %s %s', 'griffinforms-form-builder'), strtolower(trim($verb)), $input_value);
                    }
                    break;
                case 'selection_count':
                    if ('' !== $input_value) {
                        $verb = $this->getEvaluatorLabel($evaluator_key) ?: $evaluator_label;
                        $payment_summary = sprintf(__('selected products count %s %s', 'griffinforms-form-builder'), strtolower(trim($verb)), $input_value);
                    }
                    break;
                case 'selected_gateway':
                    if ('' !== $input_value) {
                        if (in_array($evaluator_key_lower, ['not_equal_to'], true)) {
                            $payment_summary = sprintf(__('gateway is not %s', 'griffinforms-form-builder'), $input_value);
                        } else {
                            $payment_summary = sprintf(__('gateway is %s', 'griffinforms-form-builder'), $input_value);
                        }
                    }
                    break;
            }

            if ('' !== trim($payment_summary)) {
                $segments = [$this->makeTextSegment($label_prefix . $payment_summary)];
                return $this->renderSummarySegments($segments, $limit);
            }
        }

        // Build the subject phrase
        $field_phrase = $field_label;
        // Address-specific condition summary map
        if ('user' === $item_type) {
            // e.g., "user browser time"
            $field_phrase = 'user';
            if ('' !== $attribute_label) {
                $field_phrase .= ' ' . strtolower($attribute_label);
            }
        } elseif ('field' === $item_type) {
            if (!preg_match('/field/i', $field_label)) {
                $field_phrase = sprintf(__('%s field', 'griffinforms-form-builder'), $field_label);
            }
        }

        if (
            '' !== $input_value
            && 'field' === $item_type
            && 'value' === $attribute_key_lower
            && 'inputdate' === strtolower((string) $field_type)
        ) {
            $input_value = $this->formatDateDisplayValue($input_value);
        }

        if ('attribute_count' === $attribute_key_lower) {
            $field_phrase = sprintf(
                __('number of answers in %s', 'griffinforms-form-builder'),
                $field_phrase
            );
        } elseif ('' !== $attribute_label) {
            if ('user' !== $item_type) {
                $field_phrase .= ' ' . strtolower($attribute_label);
            }
        }

        $field_phrase = trim($field_phrase);
        if ('' !== $field_phrase && !preg_match('/^the\s/i', $field_phrase)) {
            $field_phrase = sprintf(__('the %s', 'griffinforms-form-builder'), $field_phrase);
        }

        $segments = [];

        $evaluator_key_lower = strtolower($evaluator_key);
        if (
            $is_address_field
            && 'field' === $item_type
            && 'value' === $attribute_key_lower
            && isset($address_condition_map[$evaluator_key_lower])
        ) {
            $component = isset($address_condition_map[$evaluator_key_lower]['component'])
                ? trim($address_condition_map[$evaluator_key_lower]['component'])
                : '';
            $verb = isset($address_condition_map[$evaluator_key_lower]['verb'])
                ? trim($address_condition_map[$evaluator_key_lower]['verb'])
                : '';
            $requires_input = !empty($address_condition_map[$evaluator_key_lower]['requires_input']);

            $field_subject = $field_label;
            if (!preg_match('/field/i', $field_subject)) {
                $field_subject = sprintf(__('%s field', 'griffinforms-form-builder'), $field_subject);
            }
            if (!preg_match('/^the\s/i', $field_subject)) {
                $field_subject = sprintf(__('the %s', 'griffinforms-form-builder'), $field_subject);
            }

            $summary_text = trim($field_subject);
            if ('' !== $component) {
                $summary_text .= ' ' . $component;
            }
            if ('' !== $verb) {
                $summary_text .= ' ' . $verb;
            }

            $segments[] = $this->makeTextSegment($summary_text);

            if ($requires_input && '' !== $input_value) {
                $segments[] = $this->makeTextSegment(' ');
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }

            return $this->renderSummarySegments($segments, $limit);
        }

        $segments[] = $this->makeTextSegment(trim($field_phrase));

        // Improve evaluator wording for user->browser_time
        if ('user' === $item_type && 'browser_time' === $attribute_key_lower) {
            $map = [
                'after'  => __('is later than', 'griffinforms-form-builder'),
                'before' => __('is earlier than', 'griffinforms-form-builder'),
            ];
            $eval_text = isset($map[strtolower($evaluator_key)]) ? $map[strtolower($evaluator_key)] : $evaluator_label;
            if ($eval_text !== '') {
                $segments[] = $this->makeTextSegment(' ' . strtolower($eval_text));
            }
        } elseif ('' !== $evaluator_label) {
            $segments[] = $this->makeTextSegment(' ' . strtolower($evaluator_label));
        } elseif ('' !== $evaluator_key) {
            $segments[] = $this->makeTextSegment(' ' . strtolower(str_replace('_', ' ', $evaluator_key)));
        }

        // Pretty-print user time minutes to 12-hour clock
        if ('user' === $item_type && 'browser_time' === $attribute_key_lower && '' !== $input_value) {
            if (is_numeric($input_value)) {
                $input_value = $this->formatMinutesDisplay((int) $input_value);
            }
        }

        if ('' !== $input_value) {
            $segments[] = $this->makeTextSegment(' ');
            $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
        }

        return $this->renderSummarySegments($segments, $limit);
    }

    // Formats minutes since midnight into a localized 12-hour clock label (e.g., 6:30 PM).
    protected function formatMinutesDisplay($minutes)
    {
        $minutes = (int) $minutes;
        if ($minutes < 0) {
            return '';
        }

        $h = (int) floor($minutes / 60);
        $m = (int) ($minutes % 60);
        $ts = mktime($h, $m, 0, 1, 1, 2000);

        if (function_exists('date_i18n')) {
            return date_i18n('g:i A', $ts);
        }

        return date('g:i A', $ts);
    }

    // Converts an action array into a truncated HTML snippet for display.
    protected function formatActionSnippet(array $action, $limit = 120, $as_clause = false)
    {
        $item_type = isset($action['item']) ? (string) $action['item'] : '';
        $field_id_raw = isset($action['item_id']) ? $action['item_id'] : '';
        $field_id = is_numeric($field_id_raw) ? (int) $field_id_raw : 0;

        $field_label = '';
        $field_type = '';
        $is_address_field = false;
        $address_value_action_map = [];
        $address_label_map = [];
        if ('field' === $item_type && $field_id > 0) {
            $field_label = $this->getFieldLabelById($field_id);
            $field_type = $this->getFieldTypeById($field_id);
            $is_address_field = ('inputaddress' === strtolower((string) $field_type));
        } elseif ('row' === $item_type && $field_id > 0) {
            $field_label = $this->getRowLabelById($field_id);
        }

        if ($is_address_field) {
            $address_value_action_map = [
                'change_country_to' => __('Change country value in', 'griffinforms-form-builder'),
                'change_city_to'    => __('Change city value in', 'griffinforms-form-builder'),
                'change_state_to'   => __('Change state / province value in', 'griffinforms-form-builder'),
            ];

            $address_label_map = [
                'change_line_1_label_to' => __('Change Address Line 1 label in', 'griffinforms-form-builder'),
                'change_line_2_label_to' => __('Change Address Line 2 label in', 'griffinforms-form-builder'),
                'change_city_label_to'   => __('Change City label in', 'griffinforms-form-builder'),
                'change_state_label_to'  => __('Change State / Province label in', 'griffinforms-form-builder'),
                'change_country_label_to'=> __('Change Country label in', 'griffinforms-form-builder'),
            ];
        }

        if ('' === $field_label) {
            $field_label = $this->getChoiceLabel('actionItemTypes', $item_type);
        }

        $field_label = trim((string) $field_label);
        if ('' === $field_label) {
            $field_label = __('field', 'griffinforms-form-builder');
        }

        $attribute_key = isset($action['attribute']) ? (string) $action['attribute'] : '';
        $attribute_label = trim((string) $this->getActionAttributeLabel($attribute_key));
        $attribute_key_lower = strtolower($attribute_key);

        $operation_key = isset($action['action']) ? (string) $action['action'] : '';
        $operation_key_lower = strtolower($operation_key);
        $operation_label = trim((string) $this->getActionOperationLabel($attribute_key, $operation_key, $field_type));
        if ('' === $operation_label && '' !== $operation_key) {
            $operation_label = ucfirst(str_replace('_', ' ', $operation_key));
        }
        $operation_label = $this->normalizeActionOperationLabel($operation_key_lower, $operation_label, strtolower((string) $attribute_key));

        $input_value = isset($action['input']) ? trim((string) $action['input']) : '';
        $is_class_operation = in_array($operation_key_lower, ['add_class', 'remove_class'], true);
        $is_message_operation = in_array($operation_key_lower, ['show_error', 'show_warning', 'show_info', 'show_popup'], true);

        if ($is_address_field) {
            $address_value_action_map = [
                'change_country_to' => __('Change country value in', 'griffinforms-form-builder'),
                'change_city_to'    => __('Change city value in', 'griffinforms-form-builder'),
                'change_state_to'   => __('Change state / province value in', 'griffinforms-form-builder'),
            ];

            $address_label_map = [
                'change_line_1_label_to' => __('Change Address Line 1 label in', 'griffinforms-form-builder'),
                'change_line_2_label_to' => __('Change Address Line 2 label in', 'griffinforms-form-builder'),
                'change_city_label_to'   => __('Change City label in', 'griffinforms-form-builder'),
                'change_state_label_to'  => __('Change State / Province label in', 'griffinforms-form-builder'),
                'change_country_label_to'=> __('Change Country label in', 'griffinforms-form-builder'),
            ];
        }

        $field_phrase = $field_label;
        if ('row' === $item_type) {
            $row_name = $field_label;
            if ('visibility' === $attribute_key_lower) {
                $field_phrase = $is_class_operation
                    ? sprintf(__('the %s row', 'griffinforms-form-builder'), $row_name)
                    : sprintf(__('the row named %s', 'griffinforms-form-builder'), $row_name);
            } elseif ('heading' === $attribute_key_lower) {
                $field_phrase = sprintf(__('the %s row heading', 'griffinforms-form-builder'), $row_name);
            } else {
                $field_phrase = sprintf(__('the %s row', 'griffinforms-form-builder'), $row_name);
            }
        } else {
        if ('field' === $item_type && !preg_match('/field/i', $field_label)) {
            $field_phrase = sprintf(__('%s field', 'griffinforms-form-builder'), $field_label);
        }
        if ('' !== $field_phrase && !preg_match('/^the\\s/i', $field_phrase)) {
            if ('field' === $item_type) {
                $field_phrase = sprintf(__('the %s', 'griffinforms-form-builder'), $field_phrase);
            } elseif ('form' === $item_type) {
                $field_phrase = sprintf(__('the %s', 'griffinforms-form-builder'), strtolower($field_phrase));
            }
        }
        }

        // $attribute_key_lower is already defined above

        $segments = [];

        if ($is_address_field && 'value' === $attribute_key_lower && isset($address_value_action_map[$operation_key_lower])) {
            $segments[] = $this->makeTextSegment($address_value_action_map[$operation_key_lower] . ' ');
            $segments[] = $this->makeTextSegment($field_phrase);
            if ('' !== $input_value) {
                $segments[] = $this->makeTextSegment(' ' . __('to', 'griffinforms-form-builder') . ' ');
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }
            return $this->renderSummarySegments($segments, $limit);
        }

        if ($is_address_field && 'label' === $attribute_key_lower && isset($address_label_map[$operation_key_lower])) {
            $segments[] = $this->makeTextSegment($address_label_map[$operation_key_lower] . ' ');
            $segments[] = $this->makeTextSegment($field_phrase);
            if ('' !== $input_value) {
                $segments[] = $this->makeTextSegment(' ' . __('to', 'griffinforms-form-builder') . ' ');
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }
            return $this->renderSummarySegments($segments, $limit);
        }

        // Custom phrasing for message operations on form/field
        if ($is_message_operation && in_array($item_type, ['form', 'field'], true)) {
            $type_map = [
                'show_error'   => __('error', 'griffinforms-form-builder'),
                'show_warning' => __('warning', 'griffinforms-form-builder'),
                'show_info'    => __('info', 'griffinforms-form-builder'),
                'show_popup'   => __('popup', 'griffinforms-form-builder'),
            ];
            $kind = isset($type_map[$operation_key_lower]) ? $type_map[$operation_key_lower] : '';

            $verb = __('Show', 'griffinforms-form-builder');
            if ($as_clause) {
                $verb = $this->lcfirstMultibyte($verb);
            }

            // Build prefix like: "Show warning message above the form" or
            // "Show warning message above the Email field". For popup: "Show popup message for ..."
            if ('show_popup' === $operation_key_lower) {
                $segments[] = $this->makeTextSegment(sprintf('%s %s %s ', $verb, __('popup message', 'griffinforms-form-builder'),
                    ('form' === $item_type) ? __('for the form', 'griffinforms-form-builder') : sprintf(__('for %s', 'griffinforms-form-builder'), $field_phrase)
                ));
            } else {
                $segments[] = $this->makeTextSegment(sprintf('%s %s %s ', $verb, sprintf(__('%s message', 'griffinforms-form-builder'), $kind),
                    ('form' === $item_type) ? __('above the form', 'griffinforms-form-builder') : sprintf(__('above %s', 'griffinforms-form-builder'), $field_phrase)
                ));
            }

            // Append: with content "..."
            $segments[] = $this->makeTextSegment(__('with content', 'griffinforms-form-builder') . ' "');
            if ('' !== $input_value) {
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }
            $segments[] = $this->makeTextSegment('"');

            return $this->renderSummarySegments($segments, $limit);
        }

        if ($is_class_operation) {
            // Prefer: "Add class <classes> to the <target>"
            if ('' !== $operation_label) {
                $verb = $operation_label;
                if ($as_clause) {
                    $verb = $this->lcfirstMultibyte($verb);
                }
                $segments[] = $this->makeTextSegment($verb);
            }
            if ('' !== $input_value) {
                $segments[] = $this->makeTextSegment(' ');
                $segments = array_merge($segments, $this->buildClassSegments($input_value));
                if ('' !== $field_phrase) {
                    $target_phrase = $field_phrase;
                    if ('row' === $item_type && 'heading' === $attribute_key_lower) {
                        // Ensure clarity that classes apply to the row heading, not the row container
                        $target_phrase = sprintf(__('the %s row heading', 'griffinforms-form-builder'), $field_label);
                    }
                    $segments[] = $this->makeTextSegment(' ' . __('to', 'griffinforms-form-builder') . ' ' . $target_phrase);
                }
            } else {
                if ('' !== $field_phrase) {
                    $segments[] = $this->makeTextSegment(' ' . $field_phrase);
                }
            }
            return $this->renderSummarySegments($segments, $limit);
        }

        if ('' !== $operation_label) {
            $verb = $operation_label;
            if ($as_clause) {
                $verb = $this->lcfirstMultibyte($verb);
            }
            $segments[] = $this->makeTextSegment($verb);
        }

        if ('' !== $field_phrase) {
            $prefix = empty($segments) ? '' : ' ';
            if ('form' === $item_type && $is_message_operation) {
                // Read naturally: "Show warning message on the form …"
                $segments[] = $this->makeTextSegment($prefix . __('on the form', 'griffinforms-form-builder'));
            } else {
                $segments[] = $this->makeTextSegment($prefix . $field_phrase);
            }
        }

        if ('value' === $attribute_key_lower) {
            if (!$is_address_field) {
                $descriptor = strtolower($attribute_label ?: __('value', 'griffinforms-form-builder'));
                $segments[] = $this->makeTextSegment(' ' . $descriptor);
            }
        } elseif ('visibility' !== $attribute_key_lower && '' !== $attribute_label) {
            $segments[] = $this->makeTextSegment(' ' . strtolower($attribute_label));
        }

        if (
            '' !== $input_value
            && 'field' === $item_type
            && 'value' === $attribute_key_lower
            && 'inputdate' === strtolower((string) $field_type)
        ) {
            $input_value = $this->formatDateDisplayValue($input_value);
        }

        if ('option_groups' === $attribute_key_lower && '' !== $input_value) {
            $groups = $this->parseOptionGroupInputValue($input_value);
            if (!empty($groups)) {
                $input_value = implode(', ', $groups);
            }
        }

        if ('option_groups' === $attribute_key_lower) {
            if (in_array($operation_key_lower, ['show', 'hide'], true)) {
                $operation_label = ucfirst($operation_key_lower);
            }

            $segments = [];
            if ('' !== $operation_label) {
                $segments[] = $this->makeTextSegment($operation_label);
            }

            $segments[] = $this->makeTextSegment(' ' . __('option groups', 'griffinforms-form-builder'));

            if ('' !== $input_value) {
                $segments[] = $this->makeTextSegment(' ');
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }

            if ('' !== $field_phrase) {
                $segments[] = $this->makeTextSegment(' ' . __('for', 'griffinforms-form-builder') . ' ' . $field_phrase);
            }

            return $this->renderSummarySegments($segments, $limit);
        }

        if ('rows' === $attribute_key_lower) {
            $row_ids = [];
            if ('' !== $input_value) {
                $decoded = json_decode($input_value, true);
                if (is_array($decoded)) {
                    $row_ids = array_map('strval', $decoded);
                }
            }

            $row_count = count($row_ids);

            if ('show_rows' === $operation_key_lower) {
                $op_phrase = sprintf(
                    _n('Show %d row', 'Show %d rows', $row_count, 'griffinforms-form-builder'),
                    $row_count
                );
            } elseif ('hide_rows' === $operation_key_lower) {
                $op_phrase = sprintf(
                    _n('Hide %d row', 'Hide %d rows', $row_count, 'griffinforms-form-builder'),
                    $row_count
                );
            } else {
                $op_phrase = $operation_label;
            }

            $segments = [];
            $segments[] = $this->makeTextSegment($op_phrase);

            if ('' !== $field_phrase) {
                $segments[] = $this->makeTextSegment(' ' . __('for', 'griffinforms-form-builder') . ' ' . $field_phrase);
            }

            return $this->renderSummarySegments($segments, $limit);
        }

        if ('redirection' === $attribute_key_lower) {
            $segments = [];
            if ('change_to' === $operation_key_lower) {
                $segments[] = $this->makeTextSegment(__('Change redirection to', 'griffinforms-form-builder'));
            } elseif ('append' === $operation_key_lower) {
                $segments[] = $this->makeTextSegment(__('Append to redirection', 'griffinforms-form-builder'));
            } else {
                $segments[] = $this->makeTextSegment(__('Modify redirection', 'griffinforms-form-builder'));
            }

            if ('' !== $input_value) {
                $segments[] = $this->makeTextSegment(' ');
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }

            return $this->renderSummarySegments($segments, $limit);
        }

        if ('submit_button_label' === $attribute_key_lower) {
            $segments = [];
            $verb = __('Change', 'griffinforms-form-builder');
            if ($as_clause) {
                $verb = $this->lcfirstMultibyte($verb);
            }
            $segments[] = $this->makeTextSegment($verb . ' ' . __('submit button label', 'griffinforms-form-builder') . ' ' . __('to', 'griffinforms-form-builder') . ' ');
            if ('' !== $input_value) {
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }
            return $this->renderSummarySegments($segments, $limit);
        }

        if ('submit_button_state' === $attribute_key_lower) {
            $segments = [];
            $verb = $operation_label !== '' ? $operation_label : ucfirst($operation_key_lower);
            if ($as_clause) {
                $verb = $this->lcfirstMultibyte($verb);
            }
            $segments[] = $this->makeTextSegment($verb . ' ' . __('submit button', 'griffinforms-form-builder'));
            return $this->renderSummarySegments($segments, $limit);
        }

        if ('' !== $input_value) {
            if ($is_message_operation) {
                $joiner = ('show_popup' === $operation_key_lower)
                    ? ' ' . __('with popup message', 'griffinforms-form-builder') . ' '
                    : ' ' . __('with message', 'griffinforms-form-builder') . ' ';
                $segments[] = $this->makeTextSegment($joiner);
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            } else {
                $joiner = ' ' . __('to', 'griffinforms-form-builder') . ' ';
                $segments[] = $this->makeTextSegment($joiner);
                $segments = array_merge($segments, $this->buildInputValueSegments($input_value));
            }
        }

        return $this->renderSummarySegments($segments, $limit);
    }

    private function lcfirstMultibyte($text)
    {
        $text = (string) $text;
        if ('' === $text) {
            return '';
        }
        $first = mb_substr($text, 0, 1);
        $rest = mb_substr($text, 1);
        return mb_strtolower($first) . $rest;
    }

    // Wraps raw text into a standardized summary segment structure.
    protected function makeTextSegment($text)
    {
        $content = (string) $text;

        return [
            'type' => 'text',
            'content' => $content,
            'plain' => $content,
        ];
    }

    // Splits input strings into text and JSON-variable segments for rendering.
    protected function buildInputValueSegments($input_value)
    {
        $value = (string) $input_value;
        if ('' === $value) {
            return [];
        }

        $parts = preg_split('/(\{[^{}]*\})/', $value, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
        if (!is_array($parts)) {
            return [$this->makeTextSegment($value)];
        }

        $segments = [];

        foreach ($parts as $part) {
            if ('' === $part) {
                continue;
            }

            if ($this->isJsonVariableToken($part)) {
                $segments[] = $this->makeVariableSegment($part);
                continue;
            }

            $segments[] = $this->makeTextSegment($part);
        }

        return $segments;
    }

    protected function parseOptionGroupInputValue($raw_value)
    {
        $raw_value = trim((string) $raw_value);
        if ('' === $raw_value) {
            return [];
        }

        $decoded = json_decode($raw_value, true);
        $candidates = [];

        if (is_array($decoded)) {
            $candidates = $decoded;
        } else {
            $candidates = preg_split('/[\n,]/', $raw_value);
        }

        if (!is_array($candidates)) {
            return [];
        }

        $groups = [];

        foreach ($candidates as $entry) {
            if (is_array($entry)) {
                $label = isset($entry['label']) ? $entry['label'] : '';
            } else {
                $label = $entry;
            }

            $label = trim((string) $label);
            if ('' === $label) {
                continue;
            }

            if (!in_array($label, $groups, true)) {
                $groups[] = $label;
            }
        }

        return $groups;
    }

    // Splits CSS class lists into monospace code segments for rendering.
    protected function buildClassSegments($input_value)
    {
        $value = trim((string) $input_value);
        if ('' === $value) {
            return [];
        }

        $tokens = preg_split('/\s+/', $value, -1, PREG_SPLIT_NO_EMPTY);
        if (!is_array($tokens) || empty($tokens)) {
            return [$this->makeCodeSegment($value)];
        }

        $segments = [];
        $first = true;

        foreach ($tokens as $token) {
            if (!$first) {
                $segments[] = $this->makeTextSegment(' ');
            }

            if ($this->isJsonVariableToken($token)) {
                $segments[] = $this->makeVariableSegment($token);
            } else {
                $segments[] = $this->makeCodeSegment($token);
            }

            $first = false;
        }

        return $segments;
    }

    // Renders a JSON variable token as a badge segment with tooltip.
    protected function makeVariableSegment($token)
    {
        $label = __('Var', 'griffinforms-form-builder');
        $title = esc_attr($token);
        $content = sprintf(
            '<span class="badge bg-light text-dark border me-1 rounded-1 align-top griffinforms-cl-var" title="%s">%s</span>',
            $title,
            esc_html($label)
        );

        return [
            'type' => 'badge',
            'content' => $content,
            'plain' => '[' . $label . ']',
        ];
    }

    // Wraps textual content in a monospace code segment.
    protected function makeCodeSegment($text)
    {
        $content = (string) $text;

        return [
            'type' => 'code',
            'content' => $content,
            'plain' => $content,
        ];
    }

    // Determines whether a string represents a supported JSON variable token.
    protected function isJsonVariableToken($candidate)
    {
        $candidate = trim((string) $candidate);
        if ('' === $candidate || $candidate[0] !== '{' || substr($candidate, -1) !== '}') {
            return false;
        }

        $decoded = json_decode($candidate, true);
        if (!is_array($decoded)) {
            return false;
        }

        return isset($decoded['type'], $decoded['attr']);
    }

    // Assembles summary segments into truncated HTML with ellipsis support.
    protected function renderSummarySegments(array $segments, $limit = 120)
    {
        $limit = (int) $limit;
        if ($limit < 1) {
            $limit = 1;
        }

        $fullPlain = $this->segmentsToPlainText($segments);
        $fullLength = $this->calculateSegmentsLength($segments);

        $output = '';
        $consumed = 0;
        $count = count($segments);

        for ($index = 0; $index < $count; $index++) {
            $segment = $segments[$index];
            $type = isset($segment['type']) ? $segment['type'] : 'text';
            $plain = isset($segment['plain']) ? (string) $segment['plain'] : '';
            $content = isset($segment['content']) ? (string) $segment['content'] : $plain;

            $length = mb_strlen($plain);

            if ($consumed + $length > $limit) {
                if ('badge' === $type || 'code' === $type) {
                    break;
                }

                $remaining = $limit - $consumed;
                if ($remaining <= 0) {
                    break;
                }

                $output .= esc_html(mb_substr($plain, 0, $remaining));
                $consumed += $remaining;
                break;
            }

            if ('badge' === $type) {
                $output .= $content;
            } elseif ('code' === $type) {
                $output .= sprintf(
                    '<code class="griffinforms-logic-summary-code">%s</code>',
                    esc_html($content)
                );
            } else {
                $output .= esc_html($content);
            }

            $consumed += $length;
        }

        $needs_ellipsis = ($consumed < $fullLength);
        if ($needs_ellipsis) {
            $output .= '&hellip;';
        }

        if ('' === $output) {
            return '';
        }

        if ('' === $fullPlain) {
            return $output;
        }

        return sprintf('<span class="griffinforms-cl-summary" title="%s">%s</span>', esc_attr($fullPlain), $output);
    }

    // Calculates the combined plain-text length of supplied summary segments.
    protected function calculateSegmentsLength(array $segments)
    {
        $length = 0;
        foreach ($segments as $segment) {
            $plain = isset($segment['plain']) ? (string) $segment['plain'] : '';
            $length += mb_strlen($plain);
        }

        return $length;
    }

    // Collapses segments into a whitespace-normalized plain-text string.
    protected function segmentsToPlainText(array $segments)
    {
        $plain = '';
        foreach ($segments as $segment) {
            $plain .= isset($segment['plain']) ? (string) $segment['plain'] : '';
        }

        return trim(preg_replace('/\s+/', ' ', $plain));
    }

    // Fetches and caches the display label for a field by its ID.
    protected function getFieldLabelById($field_id)
    {
        $field_id = is_numeric($field_id) ? (int) $field_id : 0;
        if ($field_id <= 0) {
            return '';
        }

        if (isset($this->field_label_cache[$field_id])) {
            return $this->field_label_cache[$field_id];
        }

        if (!is_object($this->sql)) {
            $this->setSql();
        }

        $label = '';
        $missing = true;

        if (is_object($this->sql) && method_exists($this->sql, 'itemValues')) {
            $values = $this->sql->itemValues($field_id, 'field', ['name', 'field_type']);
            if (is_object($values)) {
                if (isset($values->name) && is_scalar($values->name)) {
                    $label = (string) $values->name;
                }
                if (isset($values->field_type) && is_scalar($values->field_type)) {
                    $this->field_type_cache[$field_id] = (string) $values->field_type;
                }
                $missing = false;
            }
        }

        if ('' === $label) {
            $label = sprintf(__('Field #%d', 'griffinforms-form-builder'), $field_id);
        }

        $this->field_label_cache[$field_id] = $label;
        $this->field_missing_cache[$field_id] = $missing;
        if (!isset($this->field_type_cache[$field_id])) {
            $this->field_type_cache[$field_id] = '';
        }

        return $label;
    }

    // Retrieves the stored field type for a specific field ID.
    protected function getFieldTypeById($field_id)
    {
        $field_id = is_numeric($field_id) ? (int) $field_id : 0;
        if ($field_id <= 0) {
            return '';
        }

        if (isset($this->field_type_cache[$field_id])) {
            return $this->field_type_cache[$field_id];
        }

        // Populate caches if not already set.
        $this->getFieldLabelById($field_id);

        return isset($this->field_type_cache[$field_id]) ? $this->field_type_cache[$field_id] : '';
    }

    protected function resolvePaymentProductLabel($field_id, $product_id)
    {
        if (!is_object($this->sql)) {
            $this->setSql();
        }

        $raw_opts = [];
        if (is_object($this->sql)) {
            $values = $this->sql->itemValues((int) $field_id, 'field', ['field_options']);
            $raw_opts = (is_object($values) && isset($values->field_options)) ? $values->field_options : [];
        }
        $decoded  = $this->maybeDecode($raw_opts);
        if (is_array($decoded)) {
            foreach ($decoded as $product) {
                if (!is_array($product) || !isset($product['id'])) {
                    continue;
                }
                if ((string) $product['id'] === (string) $product_id) {
                    return isset($product['name']) ? (string) $product['name'] : (string) $product_id;
                }
            }
        }

        return (string) $product_id;
    }

    protected function resolvePaymentGatewayLabel($gateway_id)
    {
        if (class_exists('\GriffinForms\Includes\Integrations\Categories\Payment')) {
            $payment_category = \GriffinForms\Includes\Integrations\Categories\Payment::getInstance();
            foreach ($payment_category->getProviderOptions() as $provider) {
                $id = isset($provider['id']) ? (string) $provider['id'] : '';
                if ('' === $id) {
                    continue;
                }
                if ($id === (string) $gateway_id) {
                    return isset($provider['label']) ? (string) $provider['label'] : $id;
                }
            }
        }

        return (string) $gateway_id;
    }

    protected function isFieldMissing($field_id)
    {
        $field_id = is_numeric($field_id) ? (int) $field_id : 0;
        if ($field_id <= 0) {
            return false;
        }

        if (!array_key_exists($field_id, $this->field_missing_cache)) {
            $this->getFieldLabelById($field_id);
        }

        return !empty($this->field_missing_cache[$field_id]);
    }

    protected function entryHasMissingTarget(array $entry)
    {
        $item_type = isset($entry['item']) ? strtolower((string) $entry['item']) : '';
        if ('field' !== $item_type) {
            return false;
        }

        $field_id_raw = isset($entry['item_id']) ? $entry['item_id'] : '';
        $field_id = is_numeric($field_id_raw) ? (int) $field_id_raw : 0;

        return $this->isFieldMissing($field_id);
    }

    protected function ruleHasMissingTargets(array $rule_data)
    {
        if (!is_array($rule_data)) {
            return false;
        }

        foreach (['all', 'any', 'actions'] as $group) {
            if (!isset($rule_data[$group]) || !is_array($rule_data[$group])) {
                continue;
            }
            foreach ($rule_data[$group] as $entry) {
                if (!is_array($entry)) {
                    continue;
                }
                if ($this->entryHasMissingTarget($entry)) {
                    return true;
                }
            }
        }

        return false;
    }

    // Formats a stored date value (timestamp or string) into a Y-m-d string.
    protected function formatDateDisplayValue($value)
    {
        $value = trim((string) $value);
        if ('' === $value) {
            return '';
        }

        $timestamp = null;
        if (is_numeric($value)) {
            $timestamp = (int) $value;
        } else {
            $parsed = strtotime($value);
            if (false !== $parsed) {
                $timestamp = (int) $parsed;
            }
        }

        if (!is_int($timestamp)) {
            return $value;
        }

        if ($timestamp === 0 && '0' !== $value) {
            return $value;
        }

        $format = $this->getDisplayDateFormat();

        if (function_exists('wp_date')) {
            $timezone = function_exists('wp_timezone') ? wp_timezone() : null;
            return wp_date($format, $timestamp, $timezone);
        }

        if (function_exists('date_i18n')) {
            return date_i18n($format, $timestamp, true);
        }

        return gmdate($format, $timestamp);
    }

    // Resolves the human-friendly label for an action attribute key.
    protected function getActionAttributeLabel($attribute)
    {
        $attribute = (string) $attribute;
        if ('' === $attribute) {
            return '';
        }

        if (!is_object($this->choices)) {
            $this->setChoices();
        }

        if (!is_object($this->choices) || !method_exists($this->choices, 'actionFieldAttributes')) {
            return $attribute;
        }

        $attribute_maps = [
            (array) $this->choices->actionFieldAttributes(''),
        ];
        if (method_exists($this->choices, 'actionFormAttributes')) {
            $attribute_maps[] = (array) $this->choices->actionFormAttributes();
        }
        if (method_exists($this->choices, 'actionRowAttributes')) {
            $attribute_maps[] = (array) $this->choices->actionRowAttributes();
        }

        foreach ($attribute_maps as $map) {
            if (isset($map[$attribute])) {
                return (string) $map[$attribute];
            }
        }

        return $attribute;
    }

    // Resolves the human-friendly label for an action operation key.
    protected function getActionOperationLabel($attribute, $operation, $field_type = '')
    {
        $attribute = (string) $attribute;
        $operation = (string) $operation;

        if ('' === $attribute || '' === $operation) {
            return '';
        }

        if (!is_object($this->choices)) {
            $this->setChoices();
        }

        if (!is_object($this->choices) || !method_exists($this->choices, 'getActionOperations')) {
            return $operation;
        }

        $operations = (array) $this->choices->getActionOperations($attribute, $field_type);
        if (isset($operations[$operation])) {
            return (string) $operations[$operation];
        }

        return $operation;
    }

    private function normalizeActionOperationLabel($operation_key, $label, $attribute_key)
    {
        $operation_key = strtolower((string) $operation_key);
        $label = trim((string) $label);

        $map = [
            'show'     => __('Show', 'griffinforms-form-builder'),
            'hide'     => __('Hide', 'griffinforms-form-builder'),
            'disabled' => __('Disable', 'griffinforms-form-builder'),
            'enabled'  => __('Enable', 'griffinforms-form-builder'),
            'change_to'=> __('Change', 'griffinforms-form-builder'),
        ];

        if (isset($map[$operation_key])) {
            return $map[$operation_key];
        }

        if ('' === $label) {
            return '';
        }

        $clean = preg_replace('/^be\s+/i', '', $label);
        $clean = trim($clean);
        if ('' === $clean) {
            $clean = $label;
        }

        return ucfirst($clean);
    }

    // Retrieves the localized date format configured in WordPress settings.
    protected function getDisplayDateFormat()
    {
        if (is_string($this->date_display_format) && '' !== $this->date_display_format) {
            return $this->date_display_format;
        }

        $format = function_exists('get_option') ? get_option('date_format') : '';
        $format = is_string($format) ? trim($format) : '';
        if ('' === $format) {
            $format = 'Y-m-d';
        }

        $this->date_display_format = $format;

        return $this->date_display_format;
    }

    // Fetches and caches the display label for a row by its ID.
    protected function getRowLabelById($row_id)
    {
        $row_id = is_numeric($row_id) ? (int) $row_id : 0;
        if ($row_id <= 0) {
            return '';
        }

        if (isset($this->row_label_cache[$row_id])) {
            return $this->row_label_cache[$row_id];
        }

        if (!is_object($this->sql)) {
            $this->setSql();
        }

        $label = '';
        if (is_object($this->sql) && method_exists($this->sql, 'getPageRowStructure')) {
            $row = $this->sql->getPageRowStructure($row_id);
            if (is_array($row) && isset($row['heading']) && is_scalar($row['heading'])) {
                $label = (string) $row['heading'];
            }
        }

        if ('' === $label) {
            $label = sprintf(__('Row #%d', 'griffinforms-form-builder'), $row_id);
        }

        $this->row_label_cache[$row_id] = $label;

        return $label;
    }

    // Fetches the DB name for a row; falls back to heading or Row #id
    protected function getRowNameById($row_id)
    {
        $row_id = is_numeric($row_id) ? (int) $row_id : 0;
        if ($row_id <= 0) {
            return '';
        }

        if (!is_object($this->sql)) {
            $this->setSql();
        }

        $name = '';
        if (is_object($this->sql) && method_exists($this->sql, 'itemValues')) {
            $values = $this->sql->itemValues($row_id, 'pagerow', ['name', 'heading']);
            if (is_object($values)) {
                if (isset($values->name) && is_scalar($values->name) && trim((string) $values->name) !== '') {
                    $name = (string) $values->name;
                } elseif (isset($values->heading) && is_scalar($values->heading)) {
                    $name = (string) $values->heading;
                }
            }
        }

        if ('' === $name) {
            $name = sprintf(__('Row #%d', 'griffinforms-form-builder'), $row_id);
        }

        return $name;
    }
}
