<?php

namespace GriffinForms\Admin\App\Html\Modals;

use GriffinForms\Traits\Singleton;
use GriffinForms\Admin\App\Html\Modals\ConditionalLogic\Requests;
use GriffinForms\Admin\App\Html\Modals\ConditionalLogic\Responses;
use GriffinForms\Admin\App\Html\Modals\ConditionalLogic\Scripts;
use GriffinForms\Admin\App\Html\Modals\ConditionalLogic\RuleSummaries;

class ConditionalLogic extends \GriffinForms\Admin\App\Html\Modals\Format
{
    use Singleton;
    use Requests;
    use Responses;
    use Scripts;
    use RuleSummaries;

    protected $sql;
    protected $choices;
    protected $has_footer = false;
    protected $required_capability = '';

    // Initializes the SQL service used for conditional logic queries.
    protected function setSql()
    {
        $this->sql = \GriffinForms\Admin\Sql\App::getInstance();
    }

    // Loads the choices helper supplying dropdown options for conditional logic.
    protected function setChoices()
    {
        $this->choices = \GriffinForms\Admin\Choices\ConditionalLogic::getInstance();
    }

    // Returns the localized modal title displayed in the header.
    protected function getModalTitle() {
        return __('Conditional Logic', 'griffinforms-form-builder') . $this->getModalTitleSuffix();
    }

    // Renders the modal body layout including list and editor sections.
    protected function renderModalBodyHtml() {
        echo '<div class="griffinforms-logic-message-container gf-notice-container mb-2"></div>';
        $this->logicListHtml();
        $this->backToListButtonHtml();
        echo '<div class="griffinforms-logic-editor-container d-flex small">';
            $this->logicEditorHtml();
            $this->subeditorHtml();
        echo '</div>';
    }

    // Outputs the container that lists existing conditional logic rules.
    protected function logicListHtml() {
        echo '<div class="griffinforms-logic-list-wrapper">
                    <p class="text-muted griffinforms-logic-rules-loading">' . esc_html__('Loading saved conditions...', 'griffinforms-form-builder') . '</p>';
                    //$this->getPlaceholderBox();
                    //$this->getPlaceholderBox();
                    //$this->getPlaceholderBox();
                    $this->addSlabHtml('rule', __('Add New Rule', 'griffinforms-form-builder'));
        echo '</div>';
    }

    // Renders a single condition or action group wrapper inside the editor.
    protected function logicGroupHtml($context, $heading, $group_key = '') {
        /* translators: %s: Context type like Condition or Action */
        $label = sprintf(__('Add %s', 'griffinforms-form-builder'), ucfirst($context));
        $group_key = is_scalar($group_key) ? (string) $group_key : '';
        $group_attr = '' !== $group_key ? ' data-gf-group="' . esc_attr($group_key) . '"' : '';
        echo '<div class="fw-medium mb-2">' . esc_html($heading) . '</div>
                <div class="griffinforms-condition-group-' . esc_attr($context) . ' p-3 rounded bg-light border mb-3"' . $group_attr . '>';
                    $data_attrs = [];
                    if ('' !== $group_key) {
                        $data_attrs['data-gf-group'] = $group_key;
                    }
                    $this->addSlabHtml($context, $label, $data_attrs);
        echo '</div>';
    }

    // Builds both the conditions and actions editor panels.
    protected function logicEditorHtml() {
        echo '<div class="griffinforms-logic-editor-wrapper d-none">
                    <div class="griffinforms-logic-editor-conditions-wrapper">';
                        $condition_headings = [
                            [
                                'heading' => __('All of the following conditions must be true', 'griffinforms-form-builder'),
                                'group'   => 'all',
                            ],
                            [
                                'heading' => __('Any one of the following conditions must be true', 'griffinforms-form-builder'),
                                'group'   => 'any',
                            ],
                        ];
                        foreach ($condition_headings as $condition_group) {
                            $heading = isset($condition_group['heading']) ? $condition_group['heading'] : '';
                            $group_key = isset($condition_group['group']) ? $condition_group['group'] : '';
                            $this->logicGroupHtml('condition', $heading, $group_key);
                        }
        echo        '</div>
                    <div class="griffinforms-logic-editor-actions-wrapper mt-4">';
                        $action_headings = [
                            [
                                'heading' => __('Actions to perform if above conditions are met', 'griffinforms-form-builder'),
                                'group'   => 'actions',
                            ],
                        ];
                        foreach ($action_headings as $action_group) {
                            $heading = isset($action_group['heading']) ? $action_group['heading'] : '';
                            $group_key = isset($action_group['group']) ? $action_group['group'] : '';
                            $this->logicGroupHtml('action', $heading, $group_key);
                        }
        echo        '</div>
                </div>';
    }

    // Renders the subeditor wrapper where individual entries are edited.
    protected function subeditorHtml() {
        echo '<div class="griffinforms-logic-subeditor-wrapper d-none border rounded position-relative" data-gf-context-type="" data-gf-rule-index="" data-gf-entry-index="" data-gf-condition-group="">';

        echo '<input type="hidden" id="griffinforms-logic-context-type" name="context_type" value="" />';
        echo '<input type="hidden" id="griffinforms-logic-context-rule" name="context_rule_index" value="" />';
        echo '<input type="hidden" id="griffinforms-logic-context-entry" name="context_entry_index" value="" />';
        echo '<input type="hidden" id="griffinforms-logic-context-group" name="condition_group" value="" />';

        $connector_html = $this->subeditorConnectorHtml();

        $this->renderSubeditorConditionBlock($connector_html);
        $this->renderSubeditorActionBlock($connector_html);

        $this->renderOverlayTrigger();
        $this->renderOverlayContainer($this->getOverlayInsertButtonHtml());

        echo '<div class="griffinforms-logic-subeditor-actions text-center p-3 mt-4">';
            $this->renderSubeditorCancelButton();
            $this->renderSubeditorSaveButton();
        echo '</div>';

        echo '</div>';
    }

    // Prints the condition-specific controls within the subeditor.
    private function renderSubeditorConditionBlock($connector_html)
    {
        echo '<div class="griffinforms-logic-subeditor-block griffinforms-logic-subeditor-block-condition" data-gf-context="condition">';

        echo '<div id="griffinforms-logic-subeditor-items-container" class="text-center mb-1 griffinforms-logic-subeditor-control-wrapper griffinforms-logic-subeditor-items-loader"></div>';
        $this->maybeRenderConnector($connector_html);

        echo '<div id="griffinforms-logic-subeditor-item-selection-wrapper" class="text-center mb-1"></div>';
        $this->maybeRenderConnector($connector_html);

        echo '<div id="griffinforms-logic-subeditor-item-attributes-wrapper" class="text-center mb-1"></div>';
        $this->maybeRenderConnector($connector_html);

        echo '<div id="griffinforms-logic-subeditor-attribute-evaluators-wrapper" class="text-center mb-1"></div>';

        echo '<div id="griffinforms-logic-subeditor-condition-input-wrapper" class="text-center mb-1"></div>';

        echo '</div>';
    }

    // Prints the action-specific controls within the subeditor.
    private function renderSubeditorActionBlock($connector_html)
    {
        echo '<div class="griffinforms-logic-subeditor-block griffinforms-logic-subeditor-block-action d-none" data-gf-context="action">';

        echo '<div id="griffinforms-logic-subeditor-action-items-container" class="text-center mb-1 griffinforms-logic-subeditor-control-wrapper griffinforms-logic-subeditor-action-items-loader"></div>';
        $this->maybeRenderConnector($connector_html);

        echo '<div id="griffinforms-logic-subeditor-action-field-wrapper" class="text-center mb-1"></div>';
        $this->maybeRenderConnector($connector_html);

        echo '<div id="griffinforms-logic-subeditor-action-attribute-wrapper" class="text-center mb-1"></div>';
        $this->maybeRenderConnector($connector_html);

        echo '<div id="griffinforms-logic-subeditor-action-operation-wrapper" class="text-center mb-1"></div>';
        echo '<div id="griffinforms-logic-subeditor-action-input-wrapper" class="text-center mb-1"></div>';

        echo '</div>';
    }

    // Outputs the button that opens the overlay helper.
    private function renderOverlayTrigger()
    {
        echo '<div class="griffinforms-logic-subeditor-overlay-trigger text-center">';
        echo '<button type="button" id="griffinforms-logic-subeditor-overlay-trigger" class="btn btn-sm gf-logic-var-trigger-btn">';
        echo '<span class="material-symbols-outlined gf-logic-var-trigger-icon" aria-hidden="true">data_object</span>';
        echo esc_html__('Insert variable', 'griffinforms-form-builder');
        echo '</button>';
        echo '</div>';
    }

    // Outputs the overlay helper modal including all form fields and preview.
    private function renderOverlayContainer($insert_button_html)
    {
        $helper_title   = __('Conditional Logic Helper', 'griffinforms-form-builder');
        $helper_summary = __('Build a dynamic variable and insert it into the current input without leaving the editor.', 'griffinforms-form-builder');
        $item_type_label = __('Item type', 'griffinforms-form-builder');
        $field_label = __('Field', 'griffinforms-form-builder');
        $attribute_label = __('Attribute', 'griffinforms-form-builder');
        $iteration_label = __('Iteration', 'griffinforms-form-builder');
        $field_placeholder = __('Select a field…', 'griffinforms-form-builder');
        $attribute_placeholder = __('Select an attribute…', 'griffinforms-form-builder');
        $iteration_placeholder = __('All iterations', 'griffinforms-form-builder');
        $json_preview_label = __('JSON preview', 'griffinforms-form-builder');
        $json_preview_placeholder = __('Select a field to generate a variable.', 'griffinforms-form-builder');
        $field_option_label = __('Field', 'griffinforms-form-builder');

        echo '<div id="griffinforms-logic-subeditor-overlay" class="griffinforms-logic-subeditor-overlay position-absolute top-0 end-0 m-3 shadow d-none bg-white rounded border p-3" role="dialog" aria-hidden="true">
                <div class="fw-medium mb-2">' . esc_html($helper_title) . '</div>
                <p class="mb-3 small text-muted">' . esc_html($helper_summary) . '</p>
                <div class="griffinforms-logic-subeditor-overlay-body">
                    <div class="mb-3">
                        <label for="griffinforms-logic-subeditor-overlay-item-type" class="form-label small fw-semibold mb-1">' . esc_html($item_type_label) . '</label>
                        <select id="griffinforms-logic-subeditor-overlay-item-type" class="griffinforms-logic-subeditor-control form-select form-select-sm">
                            <option value="field" selected>' . esc_html($field_option_label) . '</option>
                        </select>
                    </div>
                    <div class="mb-3">
                        <label for="griffinforms-logic-subeditor-overlay-field" class="form-label small fw-semibold mb-1">' . esc_html($field_label) . '</label>
                        <select id="griffinforms-logic-subeditor-overlay-field" class="griffinforms-logic-subeditor-control form-select form-select-sm" disabled>
                            <option value="">' . esc_html($field_placeholder) . '</option>
                        </select>
                    </div>
                    <div class="mb-3">
                        <label for="griffinforms-logic-subeditor-overlay-attribute" class="form-label small fw-semibold mb-1">' . esc_html($attribute_label) . '</label>
                        <select id="griffinforms-logic-subeditor-overlay-attribute" class="griffinforms-logic-subeditor-control form-select form-select-sm" disabled>
                            <option value="">' . esc_html($attribute_placeholder) . '</option>
                        </select>
                    </div>
                    <div class="mb-3 d-none" id="griffinforms-logic-subeditor-overlay-iteration-group">
                        <label for="griffinforms-logic-subeditor-overlay-iteration" class="form-label small fw-semibold mb-1">' . esc_html($iteration_label) . '</label>
                        <select id="griffinforms-logic-subeditor-overlay-iteration" class="griffinforms-logic-subeditor-control form-select form-select-sm" disabled>
                            <option value="">' . esc_html($iteration_placeholder) . '</option>
                        </select>
                    </div>
                    <div class="mb-3">
                        <div class="d-flex justify-content-between align-items-center mb-1">
                            <span class="form-label small fw-semibold mb-0">' . esc_html($json_preview_label) . '</span>
                            <span class="spinner-border spinner-border-sm text-secondary d-none" role="status" aria-hidden="true" id="griffinforms-logic-subeditor-overlay-spinner"></span>
                        </div>
                        <pre id="griffinforms-logic-subeditor-overlay-preview" class="border rounded small font-monospace p-2 mb-0" aria-live="polite">' . esc_html($json_preview_placeholder) . '</pre>
                    </div>
                    <div class="text-end">
                        ' . $insert_button_html . '
                    </div>
                </div>
            </div>';
    }

    // Generates the overlay insert button markup, using shared button helpers when available.
    private function getOverlayInsertButtonHtml()
    {
        $label = __('Insert variable', 'griffinforms-form-builder');

        if (is_object($this->buttons)) {
            return $this->buttons->bsBtn([
                'id'    => 'griffinforms-logic-subeditor-overlay-insert',
                'class' => 'gf-btn-light griffinforms-app-fe-btn is-disabled',
                'label' => $label,
                'data'  => ['data-gf-disabled="1"']
            ], false);
        }

        return sprintf(
            '<button type="button" id="griffinforms-logic-subeditor-overlay-insert" class="btn btn-sm btn-primary" disabled>%s</button>',
            esc_html($label)
        );
    }

    // Echoes the connector HTML between stacked subeditor controls.
    private function maybeRenderConnector($connector_html)
    {
        if ('' !== $connector_html) {
            echo $connector_html;
        }
    }

    // Prints the Save button at the bottom of the subeditor.
    protected function renderSubeditorSaveButton()
    {
        if (is_object($this->buttons)) {
            $this->buttons->bsBtn([
                'id'    => 'griffinforms-logic-subeditor-save',
                'class' => 'gf-btn-primary me-2',
                'label' => __('Save', 'griffinforms-form-builder'),
            ]);
            return;
        }

        echo '<button type="button" id="griffinforms-logic-subeditor-save" class="btn btn-primary me-2">' . esc_html__('Save', 'griffinforms-form-builder') . '</button>';
    }

    // Prints the Cancel button at the bottom of the subeditor.
    protected function renderSubeditorCancelButton()
    {
        if (is_object($this->buttons)) {
            $this->buttons->bsBtn([
                'id'    => 'griffinforms-logic-subeditor-cancel',
                'class' => 'gf-btn-light',
                'label' => __('Cancel', 'griffinforms-form-builder'),
            ]);
            return;
        }

        echo '<button type="button" id="griffinforms-logic-subeditor-cancel" class="btn btn-secondary">' . esc_html__('Cancel', 'griffinforms-form-builder') . '</button>';
    }

    // Supplies the spacer/connector markup used between stacked controls.
    protected function subeditorConnectorHtml()
    {
        $arrow_url = '';
        if (is_object($this->config) && method_exists($this->config, 'getRootUrl')) {
            $arrow_url = rtrim($this->config->getRootUrl(), '/') . '/admin/images/connector_arrow.svg';
        }

        if ('' !== $arrow_url && is_object($this->config) && method_exists($this->config, 'getRootUrl')) {
            $tip_url = rtrim($this->config->getRootUrl(), '/') . '/admin/images/connector_arrow_tip.svg';
            $shaft_img = '<img src="' . esc_url($arrow_url) . '" alt="" class="griffinforms-logic-subeditor-connector-shaft" />';
            $tip_img = '<img src="' . esc_url($tip_url) . '" alt="" class="griffinforms-logic-subeditor-connector-tip" />';
            $arrow_html = $shaft_img . $tip_img;
        } else {
            $arrow_html = '<span class="griffinforms-logic-subeditor-connector-fallback" aria-hidden="true">↓</span>';
        }

        return '<div class="griffinforms-logic-subeditor-connector" aria-hidden="true">' . $arrow_html . '</div>';
    }

    // Outputs a configurable dropdown wrapper used throughout the subeditor.
    protected function subeditorDropdown($label = '', $options = [], $contains = 'items', $selected = null, array $args = [])
    {
        $base_id = 'griffinforms-logic-subeditor-' . $contains;
        $name = $contains;
        switch ($contains) {
            case 'items':
                $name = 'item';
                break;
            case 'attributes':
                $name = 'attribute';
                break;
            case 'attribute-evaluators':
                $name = 'evaluator';
                break;
            default:
                $name = '';
                break;
        }

        $defaults = [
            'wrapper_id'      => $base_id . '-wrapper',
            'wrapper_classes' => 'text-center griffinforms-logic-subeditor-control-wrapper',
            'select_id'       => $base_id,
            'select_classes'  => 'griffinforms-logic-subeditor-control',
            'select_name'     => $name,
            'option_attrs'    => [],
        ];

        $settings = $defaults;
        foreach ($args as $key => $value) {
            if (array_key_exists($key, $defaults)) {
                $settings[$key] = $value;
            }
        }

        $wrapper_attrs = [];
        $wrapper_id = trim((string) $settings['wrapper_id']);
        if ('' !== $wrapper_id) {
            $wrapper_attrs[] = 'id="' . esc_attr($wrapper_id) . '"';
        }

        $wrapper_class = trim((string) $settings['wrapper_classes']);
        if ('' !== $wrapper_class) {
            $wrapper_attrs[] = 'class="' . esc_attr($wrapper_class) . '"';
        }

        $wrapper_attr_html = empty($wrapper_attrs) ? '' : ' ' . implode(' ', $wrapper_attrs);
        echo '<div';
        if ('' !== $wrapper_attr_html) {
            echo $wrapper_attr_html;
        }
        echo '>';
        if ('' !== trim((string) $label)) {
            echo '<div class="griffinforms-logic-subeditor-label">' . esc_html($label) . '</div>';
        }

        $select_attrs = [];
        $select_id = trim((string) $settings['select_id']);
        if ('' !== $select_id) {
            $select_attrs[] = 'id="' . esc_attr($select_id) . '"';
        }

        $select_class = trim((string) $settings['select_classes']);
        if ('' !== $select_class) {
            $select_attrs[] = 'class="' . esc_attr($select_class) . '"';
        }

        $select_name = trim((string) $settings['select_name']);
        if ('' !== $select_name) {
            $select_attrs[] = 'name="' . esc_attr($select_name) . '"';
        }

        $selected_value = null === $selected ? null : (string) $selected;
        $option_attrs_map = is_array($settings['option_attrs']) ? $settings['option_attrs'] : [];

        $select_attr_html = empty($select_attrs) ? '' : ' ' . implode(' ', $select_attrs);
        echo '<select';
        if ('' !== $select_attr_html) {
            echo $select_attr_html;
        }
        echo '>';
        if (is_array($options)) {
            foreach ($options as $item => $text) {
                if (!is_scalar($item)) {
                    continue; // Skip invalid keys
                }
                $item = (string) $item;
                $selected_attr = (null !== $selected_value && $item === $selected_value)
                    ? ' selected="selected"'
                    : '';
                $option_attr_html = '';
                if (isset($option_attrs_map[$item]) && is_array($option_attrs_map[$item])) {
                    foreach ($option_attrs_map[$item] as $attr_name => $attr_value) {
                        if ('' === $attr_name || is_array($attr_value)) {
                            continue;
                        }
                        $option_attr_html .= ' ' . esc_attr($attr_name) . '="' . esc_attr((string) $attr_value) . '"';
                    }
                }
                echo '<option value="' . esc_attr($item) . '"' . $selected_attr . $option_attr_html . '>';
                echo esc_html((string) $text);
                echo '</option>';
            }
        }
        echo '</select></div>';
    }

    // Returns a standard notice when there are no entries to display.
    protected function getEmptyNoticeHtml($message)
    {
        return '<div class="griffinforms-logic-empty text-muted small">' . esc_html($message) . '</div>';
    }

    // Renders entry markup using a callback and falls back to an empty notice when needed.
    protected function renderEntriesWithFallback(array $entries, callable $renderer, array $base_args, $empty_message)
    {
        $html = '';

        foreach ($entries as $index => $entry) {
            if (!is_array($entry)) {
                continue;
            }

            $params = array_merge([$entry], $base_args, [$index]);
            $html .= call_user_func_array($renderer, $params);
        }

        if ('' === trim($html)) {
            $html = $this->getEmptyNoticeHtml($empty_message);
        }

        return $html;
    }

    // Outputs a skeleton placeholder box used while content loads.
    protected function placeholderBoxHtml() {
        echo '<div class="p-2 mb-2 border rounded-1">
                    <div class="placeholder-glow">
                        <span class="placeholder col-12 rounded-2 griffinforms-logic-placeholder-line"></span>
                    </div>
                </div>';
    }

    // Builds the "add" slab button used for creating new conditions or actions.
    protected function addSlabHtml($context, $label, array $data_attrs = []) {
        $context = (string) $context;
        $attributes = [
            'class="griffinforms-add-' . esc_attr($context) . '-slab text-center p-2 mb-2 border border-dashed rounded-2"',
            'role="button"',
            'data-gf-context="' . esc_attr($context) . '"',
        ];

        foreach ($data_attrs as $attr_name => $attr_value) {
            if ('' === $attr_name) {
                continue;
            }

            if (is_array($attr_value)) {
                continue;
            }

            $attributes[] = esc_attr($attr_name) . '="' . esc_attr((string) $attr_value) . '"';
        }

        echo '<div ' . implode(' ', $attributes) . '>
                    <span class="dashicons dashicons-plus-alt2"></span>
                    <small class="text-muted ms-1">' . esc_html($label) . '</small>
                </div>';
    }

    // Renders the navigation header for returning to the rule list view.
    protected function backToListButtonHtml() {
        echo '<div class="griffinforms-logic-editor-header d-flex align-items-center gap-3 mb-3 d-none">
                    <button type="button" class="griffinforms-logic-back griffinforms-back-to-list">
                        <span class="griffinforms-logic-back-icon">&larr;</span>
                        <span class="griffinforms-logic-back-text">' . esc_html__('Back to Rules', 'griffinforms-form-builder') . '</span>
                    </button>
                    <div class="griffinforms-active-rule-title text-muted d-none"></div>
                </div>';
    }


    // Constructs the condition input control based on attribute metadata.
    protected function buildConditionInputHtml($input_type, $item_type, $attr, $condition, array $context = []) {
        $defaults = [
            'form_id'     => 0,
            'field_id'    => 0,
            'field_type'  => '',
            'id'          => 'griffinforms-condition-value',
            'name'        => 'input',
            'value'       => '',
            'rows'        => 3,
            'class'       => 'griffinforms-condition-input form-control',
            'placeholder' => __('Enter a value', 'griffinforms-form-builder'),
        ];

        $settings = array_merge($defaults, $context);

        $resolved_input_type = is_string($input_type) && $input_type !== ''
            ? strtolower($input_type)
            : 'textarea';

        $supported_inputs = ['text', 'email', 'url', 'number', 'date', 'time', 'textarea', 'time_select', 'select'];
        if (!in_array($resolved_input_type, $supported_inputs, true)) {
            $resolved_input_type = 'textarea';
        }

        $options = [];
        $multiple = false;
        if (!empty($settings['options']) && is_array($settings['options'])) {
            $options = $settings['options'];
        }
        if (!empty($settings['multiple'])) {
            $multiple = (bool) $settings['multiple'];
        }

        if ('date' === $resolved_input_type) {
            $raw_value = $settings['value'];
            $timestamp = null;

            if (is_numeric($raw_value)) {
                $timestamp = (int) $raw_value;
            } elseif (is_string($raw_value) && '' !== trim($raw_value)) {
                $timestamp = strtotime($raw_value);
            }

            if (!empty($timestamp)) {
                $settings['value'] = gmdate('Y-m-d', (int) $timestamp);
            }

            if ('' === trim((string) $settings['placeholder'])) {
                $settings['placeholder'] = __('Select a date', 'griffinforms-form-builder');
            }
        }

        $data_attrs = [
            'data-gf-item-type'  => (string) $item_type,
            'data-gf-attribute'  => (string) $attr,
            'data-gf-condition'  => (string) $condition,
            'data-gf-form-id'    => (int) $settings['form_id'],
            'data-gf-field-id'   => (int) $settings['field_id'],
            'data-gf-field-type' => (string) $settings['field_type'],
            'data-gf-input-type' => (string) $resolved_input_type,
        ];

        if ('date' === $resolved_input_type) {
            $data_attrs['data-gf-date-format'] = 'yy-mm-dd';
        }

        $data_attrs_html = '';
        foreach ($data_attrs as $attribute => $value) {
            if ('' === $value && '0' !== (string) $value) {
                continue;
            }
            $data_attrs_html .= sprintf(' %s="%s"', esc_attr($attribute), esc_attr((string) $value));
        }

        $class_attr = trim((string) $settings['class']);
        if ('' === $class_attr) {
            $class_attr = $defaults['class'];
        }
        $class_attr = trim($class_attr . ' griffinforms-logic-subeditor-control');

        $name_attr = trim((string) $settings['name']);
        if ('' === $name_attr) {
            $name_attr = $defaults['name'];
        }

        $id_attr = trim((string) $settings['id']);
        if ('' === $id_attr) {
            $id_attr = $defaults['id'];
        }

        $placeholder = (string) $settings['placeholder'];
        $inline_style = ' style="min-width: auto !important;"';

        $rows = (int) $settings['rows'];
        if ($rows <= 0) {
            $rows = $defaults['rows'];
        }

        if ('select' === $resolved_input_type) {
            $html  = '<select';
            $html .= ' id="' . esc_attr($id_attr) . '"';
            $html .= ' name="' . esc_attr($name_attr) . ($multiple ? '[]' : '') . '"';
            $select_classes = preg_replace('/\bform-control\b/', 'form-select', $class_attr);
            $html .= ' class="' . esc_attr($select_classes . ' griffinforms-logic-subeditor-control') . '"';
            if ($multiple) {
                $html .= ' multiple';
            }
            $html .= $data_attrs_html;
            $html .= '>';

            foreach ($options as $value => $label) {
                $selected = '';
                $current = $settings['value'];
                if ($multiple && is_array($current) && in_array((string) $value, array_map('strval', $current), true)) {
                    $selected = ' selected="selected"';
                } elseif (!$multiple && (string) $current === (string) $value) {
                    $selected = ' selected="selected"';
                }
                $html .= '<option value="' . esc_attr((string) $value) . '"' . $selected . '>' . esc_html((string) $label) . '</option>';
            }

            $html .= '</select>';
            return $html;
        }

        if ('time_select' === $resolved_input_type) {
            $name_attr = trim((string) $settings['name']);
            if ('' === $name_attr) {
                $name_attr = $defaults['name'];
            }
            $id_attr = trim((string) $settings['id']);
            if ('' === $id_attr) {
                $id_attr = $defaults['id'];
            }
            $class_attr = trim((string) $settings['class']);
            if ('' === $class_attr) {
                $class_attr = $defaults['class'];
            }
            $class_attr = preg_replace('/\bform-control\b/', 'form-select', $class_attr);

            $current_value = trim((string) $settings['value']);
            $current_minutes = is_numeric($current_value) ? (int) $current_value : -1;

            $html  = '<select';
            $html .= ' id="' . esc_attr($id_attr) . '"';
            $html .= ' name="' . esc_attr($name_attr) . '"';
            $html .= ' class="' . esc_attr($class_attr . ' griffinforms-logic-subeditor-control') . '"';
            $html .= $data_attrs_html;
            $html .= '>';

            for ($h = 0; $h < 24; $h++) {
                foreach ([0, 30] as $m) {
                    $minutes = $h * 60 + $m;
                    $label = date_i18n('g:i A', mktime($h, $m, 0, 1, 1, 2000));
                    $selected = ($current_minutes === $minutes) ? ' selected="selected"' : '';
                    $html .= '<option value="' . esc_attr((string) $minutes) . '"' . $selected . '>' . esc_html($label) . '</option>';
                }
            }

            $html .= '</select>';

            return $html;
        }

        if ('textarea' === $resolved_input_type || 'text' === $resolved_input_type || 'email' === $resolved_input_type || 'url' === $resolved_input_type) {
            $rich_classes = preg_replace('/\bform-control\b/', '', $class_attr);
            $rich_classes = preg_replace('/\s+/', ' ', trim($rich_classes));
            $html  = '<div';
            $html .= ' class="' . esc_attr($rich_classes . ' gf-logic-rich-input') . '"';
            $html .= ' id="' . esc_attr($id_attr) . '"';
            $html .= ' contenteditable="true"';
            $html .= ' role="textbox"';
            $html .= ' aria-multiline="true"';
            $html .= ' data-gf-rich-input="1"';
            $html .= ' data-gf-placeholder="' . esc_attr($placeholder) . '"';
            $html .= $data_attrs_html;
            $html .= '></div>';
            $html .= '<input type="hidden"';
            $html .= ' name="' . esc_attr($name_attr) . '"';
            $html .= ' id="' . esc_attr($id_attr) . '-hidden"';
            $html .= ' class="' . esc_attr($rich_classes . ' gf-logic-rich-input-hidden') . '"';
            $html .= ' value="' . esc_attr((string) $settings['value']) . '"';
            $html .= ' data-gf-rich-input-hidden="1"';
            $html .= $data_attrs_html;
            $html .= ' />';
        } else {
            // Date uses text input with datepicker, time uses native HTML5 time input
            $html_input_type = 'date' === $resolved_input_type ? 'text' : $resolved_input_type;
            $html  = '<input type="' . esc_attr($html_input_type) . '"';
            $html .= ' id="' . esc_attr($id_attr) . '"';
            $html .= ' name="' . esc_attr($name_attr) . '"';
            $html .= ' class="' . esc_attr($class_attr) . '"';
            $html .= ' value="' . esc_attr((string) $settings['value']) . '"';
            // Time inputs don't need placeholder
            if ('time' !== $resolved_input_type) {
                $html .= ' placeholder="' . esc_attr($placeholder) . '"';
            }
            $html .= $inline_style;
            $html .= $data_attrs_html;
            $html .= ' />';
        }

        return apply_filters('griffinforms_condition_input_html', $html, $item_type, $attr, $condition, $settings);
    }

    // Constructs the action input control based on selected operation metadata.
    protected function buildActionInputHtml($input_type, $attribute, $operation, array $context = []) {
        $defaults = [
            'form_id'     => 0,
            'field_id'    => 0,
            'field_type'  => '',
            'id'          => 'griffinforms-action-value',
            'name'        => 'input',
            'value'       => '',
            'rows'        => 3,
            'class'       => 'griffinforms-action-input form-control',
            'placeholder' => __('Enter a value', 'griffinforms-form-builder'),
        ];

        $settings = array_merge($defaults, $context);

        $resolved_input_type = is_string($input_type) && '' !== $input_type
            ? strtolower($input_type)
            : 'textarea';

        $data_attrs = [
            'data-gf-attribute'  => (string) $attribute,
            'data-gf-operation'  => (string) $operation,
            'data-gf-form-id'    => (int) $settings['form_id'],
            'data-gf-field-id'   => (int) $settings['field_id'],
            'data-gf-field-type' => (string) $settings['field_type'],
            'data-gf-input-type' => (string) $resolved_input_type,
        ];

        $data_attrs_html = '';
        foreach ($data_attrs as $attr_name => $attr_value) {
            if ('' === $attr_value && '0' !== (string) $attr_value) {
                continue;
            }
            $data_attrs_html .= sprintf(' %s="%s"', esc_attr($attr_name), esc_attr((string) $attr_value));
        }

        $class_attr = trim((string) $settings['class']);
        if ('' === $class_attr) {
            $class_attr = $defaults['class'];
        }
        $class_attr = trim($class_attr . ' griffinforms-logic-subeditor-control');

        $name_attr = trim((string) $settings['name']);
        if ('' === $name_attr) {
            $name_attr = $defaults['name'];
        }

        $id_attr = trim((string) $settings['id']);
        if ('' === $id_attr) {
            $id_attr = $defaults['id'];
        }

        $placeholder = (string) $settings['placeholder'];
        $inline_style = ' style="min-width: auto !important;"';

        $rows = (int) $settings['rows'];
        if ($rows <= 0) {
            $rows = $defaults['rows'];
        }

        // Select support (e.g., gateway chooser).
        if ('select_gateway' === $resolved_input_type || 'select' === $resolved_input_type) {
            $options = isset($settings['options']) && is_array($settings['options'])
                ? $settings['options']
                : [];

            $html  = '<select';
            $html .= ' id="' . esc_attr($id_attr) . '"';
            $html .= ' name="' . esc_attr($name_attr) . '"';
            $select_classes = preg_replace('/\bform-control\b/', 'form-select', $class_attr);
            $html .= ' class="' . esc_attr($select_classes . ' griffinforms-logic-subeditor-control') . '"';
            $html .= $data_attrs_html;
            $html .= '>';

            foreach ($options as $value => $label) {
                $selected = ((string) $settings['value'] === (string) $value) ? ' selected="selected"' : '';
                $html .= '<option value="' . esc_attr((string) $value) . '"' . $selected . '>' . esc_html((string) $label) . '</option>';
            }

            $html .= '</select>';

            return apply_filters('griffinforms_action_input_html', $html, $attribute, $operation, $settings);
        }

        $rich_action_classes = preg_replace('/\bform-control\b/', '', $class_attr);
        $rich_action_classes = preg_replace('/\s+/', ' ', trim($rich_action_classes));
        $html  = '<div';
        $html .= ' class="' . esc_attr($rich_action_classes . ' gf-logic-rich-input') . '"';
        $html .= ' id="' . esc_attr($id_attr) . '"';
        $html .= ' contenteditable="true"';
        $html .= ' role="textbox"';
        $html .= ' aria-multiline="true"';
        $html .= ' data-gf-rich-input="1"';
        $html .= ' data-gf-placeholder="' . esc_attr($placeholder) . '"';
        $html .= $data_attrs_html;
        $html .= '></div>';
        $html .= '<input type="hidden"';
        $html .= ' name="' . esc_attr($name_attr) . '"';
        $html .= ' id="' . esc_attr($id_attr) . '-hidden"';
        $html .= ' class="' . esc_attr($rich_action_classes . ' gf-logic-rich-input-hidden') . '"';
        $html .= ' value="' . esc_attr((string) $settings['value']) . '"';
        $html .= ' data-gf-rich-input-hidden="1"';
        $html .= $data_attrs_html;
        $html .= ' />';

        return apply_filters('griffinforms_action_input_html', $html, $attribute, $operation, $settings);
    }


    // Generates the field selection dropdown used when referencing other fields.
    protected function getFieldList($form_id = 0, $selected = null, array $dropdown_args = [], $label = null)
    {
        $form_id = (int) $form_id;
        $field_ids = $this->sql->fieldsInForm($form_id);
        $options = [];
        $option_attrs = [];

        if (is_array($field_ids)) {
            foreach ($field_ids as $field_id) {
                $values = $this->sql->itemValues($field_id, 'field', ['name', 'field_type', 'field_options', 'field_settings']);

                if (!is_object($values) || !isset($values->name, $values->field_type)) {
                    continue;
                }

                $field_id = (int) $field_id;
                $field_name = is_scalar($values->name) ? (string) $values->name : '';
                $field_type = is_scalar($values->field_type) ? (string) $values->field_type : '';

                $options[$field_id] = $field_name;
                $option_attrs[$field_id] = [
                    'data-gf-field-type' => $field_type,
                ];

                if ('selectfield' === strtolower($field_type) && isset($values->field_options)) {
                    $groups = $this->normalizeSelectFieldOptionGroups($values->field_options);
                    if (!empty($groups)) {
                        $encoded = wp_json_encode($groups);
                        if (false !== $encoded) {
                            $option_attrs[$field_id]['data-gf-option-groups'] = $encoded;
                            $option_attrs[$field_id]['data-gf-has-option-groups'] = '1';
                        }
                    }
                }

                if ('inputlikert' === strtolower($field_type) && isset($values->field_settings)) {
                    $rows = $this->normalizeLikertRows($values->field_settings);
                    if (!empty($rows)) {
                        $encoded = wp_json_encode($rows);
                        if (false !== $encoded) {
                            $option_attrs[$field_id]['data-gf-likert-rows'] = $encoded;
                        }
                    }
                }
            }
        }

        if (empty($options)) {
            return '<div class="griffinforms-logic-subeditor-empty gf-text-danger small" data-gf-field-list-empty="1">' . esc_html__('This form has no available fields. Add a field to configure conditional logic.', 'griffinforms-form-builder') . '</div>';
        }

        ob_start();
        $this->subeditorDropdown(
            $label ? (string) $label : __('named', 'griffinforms-form-builder'),
            $options,
            'field-items',
            null !== $selected && '' !== $selected ? (string) $selected : null,
            array_merge(
                [
                    'wrapper_id'      => '',
                    'wrapper_classes' => 'griffinforms-logic-subeditor-control-wrapper',
                    'select_id'       => '',
                    'select_classes'  => 'griffinforms-logic-subeditor-control',
                    'select_name'     => 'item_id',
                    'option_attrs'    => $option_attrs,
                ],
                $dropdown_args
            )
        );

        return ob_get_clean();
    }

    // Generates the row selection dropdown used when targeting rows.
    protected function getRowList($form_id = 0, $selected = null, array $dropdown_args = [], $label = null)
    {
        $form_id = (int) $form_id;
        $options = [];
        $option_attrs = [];

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

        if (is_object($this->sql) && method_exists($this->sql, 'getFormStructure')) {
            $structure = $this->sql->getFormStructure($form_id);
            if (is_array($structure) && isset($structure['pages']) && is_array($structure['pages'])) {
                foreach ($structure['pages'] as $page) {
                    if (!isset($page['rows']) || !is_array($page['rows'])) {
                        continue;
                    }
                    foreach ($page['rows'] as $row) {
                        if (!isset($row['id'])) {
                            continue;
                        }
                        $row_id = (int) $row['id'];
                        $label = '';
                        if (isset($row['name']) && is_scalar($row['name'])) {
                            $label = trim((string) $row['name']);
                        }
                        if ('' === $label && isset($row['heading']) && is_scalar($row['heading'])) {
                            $label = (string) $row['heading'];
                        }
                        if ('' === $label) {
                            $label = sprintf(__('Row #%d', 'griffinforms-form-builder'), $row_id);
                        }
                        $options[$row_id] = $label;
                        // Provide a generic type marker so downstream code can read something sensible
                        $option_attrs[$row_id] = [
                            'data-gf-field-type' => 'row',
                        ];
                    }
                }
            }
        }

        if (empty($options)) {
            return '<div class="griffinforms-logic-subeditor-empty gf-text-danger small" data-gf-field-list-empty="1">' . esc_html__('This form has no available rows. Add a row to configure conditional logic.', 'griffinforms-form-builder') . '</div>';
        }

        ob_start();
        $this->subeditorDropdown(
            $label ? (string) $label : __('named', 'griffinforms-form-builder'),
            $options,
            'row-items',
            null !== $selected && '' !== $selected ? (string) $selected : null,
            array_merge(
                [
                    'wrapper_id'      => '',
                    'wrapper_classes' => 'griffinforms-logic-subeditor-control-wrapper',
                    'select_id'       => 'griffinforms-logic-subeditor-action-field',
                    'select_classes'  => 'griffinforms-logic-subeditor-control',
                    'select_name'     => 'item_id',
                    'option_attrs'    => $option_attrs,
                ],
                $dropdown_args
            )
        );

        return ob_get_clean();
    }

    // Returns a static selection for current user in condition editor
    protected function getUserList($form_id = 0)
    {
        $html  = '<div class="text-center griffinforms-logic-subeditor-control-wrapper">';
        $html .= '<div class="form-text text-muted mb-2">' . esc_html__('This condition targets the current user context (browser).', 'griffinforms-form-builder') . '</div>';
        $html .= '<div class="fw-semibold mb-2">' . esc_html__('Current user', 'griffinforms-form-builder') . '</div>';
        $html .= '<input type="hidden" id="griffinforms-logic-subeditor-item-selection" name="item_id" value="current_user" />';
        $html .= '</div>';
        return $html;
    }

    // Normalizes select field option groups into a simple list of labels.
    protected function normalizeSelectFieldOptionGroups($raw_options)
    {
        $decoded = $this->maybeDecode($raw_options);
        if (!is_array($decoded) || empty($decoded)) {
            return [];
        }

        $groups = [];

        foreach ($decoded as $group_label => $entries) {
            if (!is_array($entries) || empty($entries)) {
                continue;
            }

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

            $hasValidOption = false;
            foreach ($entries as $option_label => $option_value) {
                if ('' !== trim((string) $option_label)) {
                    $hasValidOption = true;
                    break;
                }
            }

            if ($hasValidOption) {
                $groups[] = $label;
            }
        }

        return $groups;
    }

    // Returns [{id, label}, ...] for rows in a Likert field_options blob.
    protected function normalizeLikertRows($raw_options)
    {
        $decoded = $this->maybeDecode($raw_options);
        if (!is_array($decoded)) {
            return [];
        }

        $rows_data = isset($decoded['rows']) && is_array($decoded['rows']) ? $decoded['rows'] : [];
        $rows = [];

        foreach ($rows_data as $row) {
            if (!is_array($row)) {
                continue;
            }
            $id    = isset($row['id'])    ? trim((string) $row['id'])    : '';
            $label = isset($row['label']) ? trim((string) $row['label']) : '';
            if ('' === $id) {
                continue;
            }
            $rows[] = ['id' => $id, 'label' => $label];
        }

        return $rows;
    }

    // Builds the HTML card summarizing a single rule in the list view.
    protected function ruleHtml(array $rule_data, $index = 0)
    {
        $conditions_all = isset($rule_data['all']) && is_array($rule_data['all']) ? count($rule_data['all']) : 0;
        $conditions_any = isset($rule_data['any']) && is_array($rule_data['any']) ? count($rule_data['any']) : 0;
        $actions_count = isset($rule_data['actions']) && is_array($rule_data['actions']) ? count($rule_data['actions']) : 0;
        $rule_title = $this->getRuleTitle($index, $rule_data);
        $rule_number = (int) $index + 1;
        $rule_prefix = sprintf(__('Rule %d:', 'griffinforms-form-builder'), $rule_number);
        $fallback_title = sprintf(__('Rule %d', 'griffinforms-form-builder'), $rule_number);
        $rule_summary = trim((string) $rule_title);
        if ($rule_summary === $fallback_title) {
            $rule_summary = '';
        }

        $meta_class = 0 === $actions_count ? 'text-muted fw-medium' : 'text-muted fw-medium';

        $html  = '<div class="griffinforms-logic-rule-item small border-bottom p-2 mb-2 d-flex align-items-center justify-content-between" data-gf-rule-index="' . esc_attr((string) $index) . '">';
        $html .= '<div class="griffinforms-logic-rule-header mb-1">';
        $html .= '<div class="mb-1"><span class="fw-bold">' . esc_html($rule_prefix) . '</span>';
        if ('' !== $rule_summary) {
            $html .= ' ' . wp_kses_post($rule_summary);
        }
        $html .= '</div>';
        $html .= '<div class="griffinforms-logic-rule-meta ' . esc_attr($meta_class) . ' small">';
        $html .= esc_html(sprintf(__('All: %d', 'griffinforms-form-builder'), $conditions_all));
        $html .= ' • ' . esc_html(sprintf(__('Any: %d', 'griffinforms-form-builder'), $conditions_any));
        $html .= ' • ' . esc_html(sprintf(__('Actions: %d', 'griffinforms-form-builder'), $actions_count));
        $html .= '</div>';
        if ($this->ruleHasMissingTargets($rule_data)) {
            $html .= '<div class="gf-text-danger small mt-1">' . esc_html__('This rule references fields that no longer exist. Review its conditions and actions.', 'griffinforms-form-builder') . '</div>';
        }
        $html .= '</div>';
        $html .= '<div class="griffinforms-logic-rule-actions d-flex align-items-center">';
        $html .= '<button type="button" class="gf-btn-light btn griffinforms-app-fe-btn me-2 griffinforms-logic-rule-edit">' . esc_html__('Edit', 'griffinforms-form-builder') . '</button>';
        $html .= '<button type="button" class="gf-btn-light btn griffinforms-app-fe-btn gf-text-danger griffinforms-logic-rule-delete">' . esc_html__('Delete', 'griffinforms-form-builder') . '</button>';
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    // Builds the HTML summary for a single condition within a rule.
    protected function conditionHtml(array $condition_data, $rule_index = 0, $group_key = 'all', $index = 0)
    {
        $rule_index = (int) $rule_index;
        $index = (int) $index;
        $group_key = is_scalar($group_key) ? strtolower((string) $group_key) : 'all';

        $semantic_summary = trim((string) $this->formatConditionSnippet($condition_data));
        $summary = '' !== $semantic_summary ? $semantic_summary : $this->buildConditionSummary($condition_data);
        $entry_payload = $this->encodeEntryPayload($condition_data);
        $condition_number = $index + 1;
        $condition_prefix = sprintf(__('Condition %d:', 'griffinforms-form-builder'), $condition_number);

        $html  = '<div class="griffinforms-logic-condition-item border rounded p-2 mb-2 d-flex align-items-center justify-content-between bg-white" data-gf-rule-index="' . esc_attr((string) $rule_index) . '" data-gf-entry-index="' . esc_attr((string) $index) . '" data-gf-condition-group="' . esc_attr($group_key) . '"';
        if ('' !== $entry_payload) {
            $html .= ' data-gf-entry="' . $entry_payload . '"';
        }
        $html .= '>';
        $html .= '<div class="griffinforms-logic-condition-summary"><strong>' . esc_html($condition_prefix) . '</strong>';
        if ('' !== $summary) {
            $html .= ' ' . wp_kses_post($summary);
        }
        if ($this->entryHasMissingTarget($condition_data)) {
            $html .= '<div class="gf-text-danger small mt-1">' . esc_html__('This target field no longer exists. Update or remove this condition.', 'griffinforms-form-builder') . '</div>';
        }
        $html .= '</div>';
        $html .= '<div class="griffinforms-logic-condition-actions d-flex align-items-center">';
        $html .= '<button type="button" class="btn gf-btn-light griffinforms-app-fe-btn me-2 griffinforms-logic-condition-edit">' . esc_html__('Edit', 'griffinforms-form-builder') . '</button>';
        $html .= '<button type="button" class="btn gf-btn-light griffinforms-app-fe-btn gf-text-danger griffinforms-logic-condition-delete">' . esc_html__('Delete', 'griffinforms-form-builder') . '</button>';
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    // Builds the HTML summary for a single action within a rule.
    protected function actionHtml(array $action_data, $rule_index = 0, $index = 0)
    {
        $rule_index = (int) $rule_index;
        $index = (int) $index;

        $semantic_summary = trim((string) $this->formatActionSnippet($action_data));
        $summary = '' !== $semantic_summary ? $semantic_summary : $this->buildActionSummary($action_data);
        $entry_payload = $this->encodeEntryPayload($action_data);
        $action_number = $index + 1;
        $action_prefix = sprintf(__('Action %d:', 'griffinforms-form-builder'), $action_number);

        $html  = '<div class="griffinforms-logic-action-item bg-white border rounded p-2 mb-2 d-flex align-items-center justify-content-between" data-gf-rule-index="' . esc_attr((string) $rule_index) . '" data-gf-entry-index="' . esc_attr((string) $index) . '"';
        if ('' !== $entry_payload) {
            $html .= ' data-gf-entry="' . $entry_payload . '"';
        }
        $html .= '>';
        $html .= '<div class="griffinforms-logic-action-summary"><strong>' . esc_html($action_prefix) . '</strong>';
        if ('' !== $summary) {
            $html .= ' ' . wp_kses_post($summary);
        }
        if ($this->entryHasMissingTarget($action_data)) {
            $html .= '<div class="gf-text-danger small mt-1">' . esc_html__('This target field no longer exists. Update or remove this action.', 'griffinforms-form-builder') . '</div>';
        }
        $html .= '</div>';
        $html .= '<div class="griffinforms-logic-action-actions d-flex align-items-center">';
        $html .= '<button type="button" class="btn gf-btn-light griffinforms-app-fe-btn me-2 griffinforms-logic-action-edit">' . esc_html__('Edit', 'griffinforms-form-builder') . '</button>';
        $html .= '<button type="button" class="btn gf-btn-light griffinforms-app-fe-btn gf-text-danger griffinforms-logic-action-delete">' . esc_html__('Delete', 'griffinforms-form-builder') . '</button>';
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    // Renders all condition entries for a rule, showing a placeholder when empty.
    protected function renderConditionEntries(array $entries, $rule_index = 0, $group_key = 'all')
    {
        return $this->renderEntriesWithFallback(
            $entries,
            [$this, 'conditionHtml'],
            [(int) $rule_index, $group_key],
            __('No conditions added yet.', 'griffinforms-form-builder')
        );
    }

    // Renders all action entries for a rule, showing a placeholder when empty.
    protected function renderActionEntries(array $entries, $rule_index = 0)
    {
        return $this->renderEntriesWithFallback(
            $entries,
            [$this, 'actionHtml'],
            [(int) $rule_index],
            __('No actions added yet.', 'griffinforms-form-builder')
        );
    }

    // Produces a human-readable summary string for a condition entry.
    protected function buildConditionSummary(array $condition)
    {
        $item_key = isset($condition['item']) ? (string) $condition['item'] : '';
        $attribute_key = isset($condition['attribute']) ? (string) $condition['attribute'] : '';
        $evaluator_key = isset($condition['evaluator']) ? (string) $condition['evaluator'] : '';
        $input_value = isset($condition['input']) ? (string) $condition['input'] : '';

        $labels = [];
        $item_label = $this->getChoiceLabel('ifItems', $item_key);
        if ('' !== $item_label) {
            $labels[] = $item_label;
        }

        $attribute_label = $this->getChoiceLabel('fieldAttrs', $attribute_key);
        if ('' !== $attribute_label) {
            $labels[] = $attribute_label;
        }

        $evaluator_label = $this->getEvaluatorLabel($evaluator_key);
        if ('' !== $evaluator_label) {
            $labels[] = $evaluator_label;
        }

        if ('' !== $input_value) {
            $labels[] = sprintf(__('Value: %s', 'griffinforms-form-builder'), $input_value);
        }

        if (empty($labels)) {
            $labels[] = __('Condition', 'griffinforms-form-builder');
        }

        return implode(' • ', array_map('trim', $labels));
    }

    // Produces a human-readable summary string for an action entry.
    protected function buildActionSummary(array $action)
    {
        $action_key = isset($action['action']) ? (string) $action['action'] : '';
        $item_key = isset($action['item']) ? (string) $action['item'] : '';
        $input_value = isset($action['input']) ? (string) $action['input'] : '';

        $labels = [];

        if ('' !== $action_key) {
            $labels[] = sprintf(__('Action: %s', 'griffinforms-form-builder'), $action_key);
        }

        if ('' !== $item_key) {
            $labels[] = sprintf(__('Item: %s', 'griffinforms-form-builder'), $item_key);
        }

        if ('' !== $input_value) {
            $labels[] = sprintf(__('Value: %s', 'griffinforms-form-builder'), $input_value);
        }

        if (empty($labels)) {
            $labels[] = __('Action', 'griffinforms-form-builder');
        }

        return implode(' • ', array_map('trim', $labels));
    }

    // Retrieves a label from the choices helper keyed by method and option key.
    protected function getChoiceLabel($method, $key)
    {
        if (!is_object($this->choices)) {
            $this->setChoices();
        }

        if (!is_object($this->choices) || !method_exists($this->choices, $method)) {
            return (string) $key;
        }

        $choices = (array) $this->choices->$method();
        if (isset($choices[$key])) {
            return (string) $choices[$key];
        }

        return (string) $key;
    }

    // Returns the label for a condition evaluator key.
    protected function getEvaluatorLabel($key)
    {
        if (!is_object($this->choices)) {
            $this->setChoices();
        }

        if (!is_object($this->choices) || !method_exists($this->choices, 'getDefaultFieldValueEvaluatorLabels')) {
            return (string) $key;
        }

        $labels = (array) $this->choices->getDefaultFieldValueEvaluatorLabels();

        if (method_exists($this->choices, 'fieldattribute_countEvaluators')) {
            $labels += (array) $this->choices->fieldattribute_countEvaluators();
        }
        if (method_exists($this->choices, 'fieldstrengthEvaluators')) {
            $labels += (array) $this->choices->fieldstrengthEvaluators();
        }
        if (isset($labels[$key])) {
            return (string) $labels[$key];
        }

        $additional_sources = array(
            'inputcheckboxvalueEvaluators',
            'termsconditionsvalueEvaluators',
        );

        foreach ($additional_sources as $method) {
            if (!method_exists($this->choices, $method)) {
                continue;
            }

            $label = $this->extractEvaluatorLabelFromConfig($this->choices->$method(), $key);
            if (null !== $label) {
                return (string) $label;
            }
        }

        return (string) $key;
    }

    private function extractEvaluatorLabelFromConfig($config, $key)
    {
        if (!is_array($config) || !array_key_exists($key, $config)) {
            return null;
        }

        $value = $config[$key];

        if (is_array($value) && isset($value['label'])) {
            return (string) $value['label'];
        }

        if (is_string($value)) {
            return $value;
        }

        return null;
    }

    // Encodes entry payloads into a JSON string suitable for embedding in markup.
    protected function encodeEntryPayload(array $payload)
    {
        if (empty($payload)) {
            return '';
        }

        $json = wp_json_encode($payload);
        if (false === $json) {
            return '';
        }

        return esc_attr($json);
    }

    // Generates a dropdown of forms for action configuration when required.
    protected function getFormList($form_id = 0)
    {
        // Sample data. Replace with real lookup later.
        return array(
            array('id' => 1, 'label' => __('Contact Us Form', 'griffinforms-form-builder')),
            array('id' => 2, 'label' => __('Feedback Form', 'griffinforms-form-builder')),
        );
    }

    private function __construct()
    {
        $this->register();
        $this->setAll();
    }
}
