<?php

namespace GriffinForms\Editors\Gutenberg;

use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * REST controller for Block Editor data (forms list + lightweight structure).
 */
class Rest
{
    public const NAMESPACE = 'griffinforms/v1';

    private const MAX_PAGES = 5;
    private const MAX_FIELDS = 50;

    /**
     * Register REST routes.
     */
    public static function registerRoutes(): void
    {
        add_action('rest_api_init', function () {
            register_rest_route(
                self::NAMESPACE,
                '/gutenberg/forms',
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array(static::class, 'getForms'),
                    'permission_callback' => array(static::class, 'canManageForms'),
                    'args'                => array(
                        'search' => array(
                            'type'              => 'string',
                            'required'          => false,
                            'sanitize_callback' => 'sanitize_text_field',
                        ),
                        'limit' => array(
                            'type'              => 'integer',
                            'required'          => false,
                            'sanitize_callback' => 'absint',
                            'default'           => 100,
                        ),
                    ),
                )
            );

            register_rest_route(
                self::NAMESPACE,
                '/gutenberg/forms/(?P<form_id>\d+)/structure',
                array(
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => array(static::class, 'getFormStructure'),
                    'permission_callback' => array(static::class, 'canManageForms'),
                    'args'                => array(
                        'form_id' => array(
                            'type'              => 'integer',
                            'required'          => true,
                            'sanitize_callback' => 'absint',
                        ),
                    ),
                )
            );
        });
    }

    /**
     * Permission guard for block-related data.
     */
    public static function canManageForms(): bool
    {
        return current_user_can('manage_options');
    }

    /**
     * Return list of forms for selector.
     */
    public static function getForms(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        global $wpdb;
        $config = \GriffinForms\Config::getInstance();
        $forms_table = $config->getTable('form');

        $search = $request->get_param('search');
        $limit = max(1, (int) $request->get_param('limit'));
        $limit = min($limit, 200);

        $sql = "SELECT id, name FROM {$forms_table}";
        $params = array();

        if (!empty($search)) {
            $like = '%' . $wpdb->esc_like($search) . '%';
            $sql .= ' WHERE name LIKE %s';
            $params[] = $like;
        }

        $sql .= ' ORDER BY id DESC';
        $sql .= $wpdb->prepare(' LIMIT %d', $limit);

        $results = empty($params)
            ? $wpdb->get_results($sql, ARRAY_A)
            : $wpdb->get_results($wpdb->prepare($sql, ...$params), ARRAY_A);

        $forms = array();

        if (is_array($results)) {
            foreach ($results as $row) {
                $forms[] = array(
                    'id'   => absint($row['id']),
                    'name' => sanitize_text_field($row['name']),
                    'form_url' => admin_url('admin.php?page=griffinforms-form&id=' . absint($row['id'])),
                    'builder_url' => admin_url('admin.php?page=griffinforms-app&form_id=' . absint($row['id'])),
                );
            }
        }

        return new WP_REST_Response($forms);
    }

    /**
     * Return lightweight form structure for editor preview.
     */
    public static function getFormStructure(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        $form_id = absint($request['form_id']);
        if ($form_id < 1) {
            return new WP_Error('griffinforms_invalid_form', __('Invalid form ID.', 'griffinforms-form-builder'), array('status' => 400));
        }

        $form_item = new \GriffinForms\Items\Form();
        if (!$form_item->loadItem($form_id)) {
            return new WP_Error('griffinforms_form_not_found', __('Form not found.', 'griffinforms-form-builder'), array('status' => 404));
        }

        $builder = new RestStructureBuilder(self::MAX_PAGES, self::MAX_FIELDS);
        $payload = $builder->build($form_item);

        return new WP_REST_Response($payload);
    }
}

/**
 * Internal helper to build sanitized structure payloads.
 */
class RestStructureBuilder
{
    private int $max_pages;
    private int $max_fields;

    private int $page_count = 0;
    private int $field_count = 0;

    public function __construct(int $max_pages, int $max_fields)
    {
        $this->max_pages = $max_pages;
        $this->max_fields = $max_fields;
    }

    public function build(\GriffinForms\Items\Form $form_item): array
    {
        $theme_id = absint($form_item->getProp('form_theme'));

        $raw_pb = $form_item->getProp('progress_bar');
        $progress_bar = is_array($raw_pb) ? array(
            'toggle'             => !empty($raw_pb['toggle']),
            'style'              => in_array($raw_pb['style'] ?? '', array('stepper', 'tabs'), true) ? $raw_pb['style'] : 'stepper',
            'show_page_headings' => !empty($raw_pb['show_page_headings']),
        ) : array('toggle' => false, 'style' => 'stepper', 'show_page_headings' => false);

        $settings         = \GriffinForms\Settings::getInstance();
        $ri_mode          = (string) $settings->getOption('required_indicator_mode', 'asterisk');
        $ri_text          = $ri_mode === 'asterisk' ? '*' : (string) $settings->getOption('required_indicator_custom_text', '');
        if ($ri_text === 'none') { $ri_text = ''; }

        $data = array(
            'id'            => absint($form_item->getProp('id')),
            'name'          => sanitize_text_field((string) $form_item->getProp('name')),
            'heading'       => sanitize_text_field((string) $form_item->getProp('heading')),
            'description'   => sanitize_textarea_field((string) $form_item->getProp('description')),
            'submit_label'  => sanitize_text_field((string) $form_item->getProp('submit_label')),
            'progress_bar'  => $progress_bar,
            'pages'         => array(),
            'truncated'     => false,
            'truncated_notice' => '',
            'theme_css'     => $this->buildThemeCss($theme_id, $progress_bar),
            'theme_fonts'   => $this->getThemeFonts($theme_id),
            'form_style_css' => '',
            'page_style_css' => '',
            'row_style_css' => '',
            'signature_canvas_style' => '',
            'richtext_editor_style'  => '',
            'likert_table_style'     => '',
            'required_indicator'     => $ri_text,
        );

        $page_ids = $form_item->getProp('formpages');
        if (!is_array($page_ids) || empty($page_ids)) {
            return $data;
        }

        foreach ($page_ids as $index => $page_id) {
            if ($this->page_count >= $this->max_pages || $this->field_count >= $this->max_fields) {
                $data['truncated'] = true;
                $data['truncated_notice'] = __('Preview limited to first 5 pages / 50 fields; view full form in builder.', 'griffinforms-form-builder');
                break;
            }

            $page_item = new \GriffinForms\Items\FormPage();
            if (!$page_item->loadItem($page_id)) {
                continue;
            }

                $raw_step_data = $page_item->getProp('step_data');
            $step_data = is_array($raw_step_data) ? array(
                'step_name' => sanitize_text_field((string) ($raw_step_data['step_name'] ?? '')),
                'step_icon' => sanitize_text_field((string) ($raw_step_data['step_icon'] ?? '')),
            ) : array('step_name' => '', 'step_icon' => '');

            $page_payload = array(
                'id'          => absint($page_id),
                'index'       => $index,
                'name'        => sanitize_text_field((string) $page_item->getProp('name')),
                'heading'     => sanitize_text_field((string) $page_item->getProp('heading')),
                'description' => sanitize_textarea_field((string) $page_item->getProp('description')),
                'step_data'   => $step_data,
                'rows'        => array(),
            );

            $this->page_count++;
            $this->buildRows($page_item, $page_payload);
            $data['pages'][] = $page_payload;

            if ($this->field_count >= $this->max_fields) {
                $data['truncated'] = true;
                $data['truncated_notice'] = __('Preview limited to first 5 pages / 50 fields; view full form in builder.', 'griffinforms-form-builder');
                break;
            }
        }

        // Attach base styles if theme is set
        $this->addBaseStyles($data, $theme_id);

        return $data;
    }

    private function buildRows(\GriffinForms\Items\FormPage $page_item, array &$page_payload): void
    {
        $row_ids = $page_item->getProp('pagerows');
        if (!is_array($row_ids) || empty($row_ids)) {
            return;
        }

        foreach ($row_ids as $row_id) {
            if ($this->field_count >= $this->max_fields) {
                return;
            }

            $row_item = new \GriffinForms\Items\PageRow();
            if (!$row_item->loadItem($row_id)) {
                continue;
            }

            $row_payload = array(
                'id'          => absint($row_id),
                'heading'     => sanitize_text_field((string) $row_item->getProp('heading')),
                'description' => sanitize_textarea_field((string) $row_item->getProp('description')),
                'columns'     => array(),
            );

            $this->buildColumns($row_item, $row_payload);
            $page_payload['rows'][] = $row_payload;

            if ($this->field_count >= $this->max_fields) {
                return;
            }
        }
    }

    private function buildColumns(\GriffinForms\Items\PageRow $row_item, array &$row_payload): void
    {
        $column_ids = $row_item->getProp('rowcolumns');
        if (!is_array($column_ids) || empty($column_ids)) {
            return;
        }

        foreach ($column_ids as $column_id) {
            if ($this->field_count >= $this->max_fields) {
                return;
            }

            $column_item = new \GriffinForms\Items\RowColumn();
            if (!$column_item->loadItem($column_id)) {
                continue;
            }

            $width = absint($column_item->getProp('width'));
            if ($width < 1 || $width > 12) {
                $width = 12;
            }

            $column_payload = array(
                'id'     => absint($column_id),
                'width'  => $width,
                'fields' => array(),
            );

            $this->buildFields($column_item, $column_payload);
            $row_payload['columns'][] = $column_payload;

            if ($this->field_count >= $this->max_fields) {
                return;
            }
        }
    }

    private function buildFields(\GriffinForms\Items\RowColumn $column_item, array &$column_payload): void
    {
        $field_ids = $column_item->getProp('fields');
        if (!is_array($field_ids) || empty($field_ids)) {
            return;
        }

        foreach ($field_ids as $field_id) {
            if ($this->field_count >= $this->max_fields) {
                return;
            }

            $field_item = new \GriffinForms\Items\Field();
            if (!$field_item->loadItem($field_id)) {
                continue;
            }

            $label = (string) $field_item->getProp('heading');
            if ($label === '') {
                $label = (string) $field_item->getProp('label');
            }

            $address_labels = array();
            $line2_enabled = true;
            if ((string) $field_item->getProp('field_type') === 'inputaddress') {
                $address_labels = $this->getAddressLabels($field_item);
                $line2_enabled = $this->getAddressLine2Enabled($field_item);
            }

            $options = $this->sanitizeOptions(
                (string) $field_item->getProp('field_type'),
                $field_item->getProp('field_options')
            );

            $terms = array();
            if ((string) $field_item->getProp('field_type') === 'termsconditions') {
                $terms = $this->getTermsSettings($field_item);
            }

            $field_settings = $field_item->getProp('field_settings');
            if (is_string($field_settings)) {
                $decoded = maybe_unserialize($field_settings);
                if (is_array($decoded)) {
                    $field_settings = $decoded;
                }
            }
            $phone_display_mode = is_array($field_settings) && !empty($field_settings['phone_display_mode'])
                ? sanitize_key((string) $field_settings['phone_display_mode'])
                : '';

            $pad_height = is_array($field_settings) && isset($field_settings['pad_height'])
                ? absint($field_settings['pad_height'])
                : 120;

            $signature_placeholder = is_array($field_settings) && isset($field_settings['signature_placeholder'])
                ? sanitize_text_field((string) $field_settings['signature_placeholder'])
                : '';

            $signature_bg_color = is_array($field_settings) && isset($field_settings['bg_color'])
                ? sanitize_hex_color((string) $field_settings['bg_color'])
                : '';

            $signature_pen_color = is_array($field_settings) && isset($field_settings['pen_color'])
                ? sanitize_hex_color((string) $field_settings['pen_color'])
                : '';

            $rating_scale = is_array($field_settings) && isset($field_settings['scale'])
                ? absint($field_settings['scale'])
                : 5;
            $rating_icon_group = is_array($field_settings) && isset($field_settings['icon_group'])
                ? sanitize_key((string) $field_settings['icon_group'])
                : 'star';
            $rating_icon_size = is_array($field_settings) && isset($field_settings['icon_size'])
                ? absint($field_settings['icon_size'])
                : 32;
            $rating_color_filled = is_array($field_settings) && !empty($field_settings['color_filled'])
                ? sanitize_hex_color((string) $field_settings['color_filled'])
                : '';
            $rating_color_empty = is_array($field_settings) && !empty($field_settings['color_empty'])
                ? sanitize_hex_color((string) $field_settings['color_empty'])
                : '';
            $rating_step_labels = is_array($field_settings) && !empty($field_settings['step_labels']) && is_array($field_settings['step_labels'])
                ? array_map('sanitize_text_field', $field_settings['step_labels'])
                : array();
            $rating_custom_image_id = is_array($field_settings) && !empty($field_settings['custom_image_id'])
                ? absint($field_settings['custom_image_id'])
                : 0;
            $rating_step_image_ids = is_array($field_settings) && !empty($field_settings['step_image_ids']) && is_array($field_settings['step_image_ids'])
                ? array_map('absint', $field_settings['step_image_ids'])
                : array();
            $rating_custom_image_url = $rating_custom_image_id ? wp_get_attachment_image_url($rating_custom_image_id, 'full') : '';
            $rating_step_image_urls = array();
            foreach ($rating_step_image_ids as $image_id) {
                $rating_step_image_urls[] = $image_id ? wp_get_attachment_image_url($image_id, 'full') : '';
            }
            $rating_allow_clear = is_array($field_settings) && !empty($field_settings['allow_clear']) ? 1 : 0;

            $likert_rows = is_array($field_settings) && !empty($field_settings['rows']) && is_array($field_settings['rows'])
                ? array_map(function($r) { return ['id' => sanitize_key((string) ($r['id'] ?? '')), 'label' => sanitize_text_field((string) ($r['label'] ?? ''))]; }, $field_settings['rows'])
                : [['id' => 'row_1', 'label' => 'Row 1'], ['id' => 'row_2', 'label' => 'Row 2'], ['id' => 'row_3', 'label' => 'Row 3']];
            $likert_columns = is_array($field_settings) && !empty($field_settings['columns']) && is_array($field_settings['columns'])
                ? array_map(function($c) { return ['id' => sanitize_key((string) ($c['id'] ?? '')), 'label' => sanitize_text_field((string) ($c['label'] ?? ''))]; }, $field_settings['columns'])
                : [['id' => 'col_1', 'label' => 'Strongly Disagree'], ['id' => 'col_2', 'label' => 'Disagree'], ['id' => 'col_3', 'label' => 'Neutral'], ['id' => 'col_4', 'label' => 'Agree'], ['id' => 'col_5', 'label' => 'Strongly Agree']];
            $likert_allow_na     = is_array($field_settings) && !empty($field_settings['allow_na']);
            $likert_column_width = is_array($field_settings) && isset($field_settings['column_width']) ? sanitize_key((string) $field_settings['column_width']) : 'auto';

            $richtext_toolbar = is_array($field_settings) && !empty($field_settings['toolbar']) && is_array($field_settings['toolbar'])
                ? array_map('sanitize_key', $field_settings['toolbar'])
                : [];
            $richtext_placeholder = is_array($field_settings) && isset($field_settings['placeholder'])
                ? sanitize_text_field((string) $field_settings['placeholder'])
                : '';
            $richtext_min_height = is_array($field_settings) && isset($field_settings['min_height'])
                ? max(50, absint($field_settings['min_height']))
                : 120;
            $imagesel_columns_desktop = is_array($field_settings) && isset($field_settings['columns_desktop'])
                ? max(1, min(6, absint($field_settings['columns_desktop'])))
                : 3;
            $imagesel_columns_mobile = is_array($field_settings) && isset($field_settings['columns_mobile'])
                ? max(1, min(3, absint($field_settings['columns_mobile'])))
                : 2;
            $imagesel_show_title = !empty($field_settings['show_title']);
            $imagesel_show_description = !empty($field_settings['show_description']);
            $imagesel_options = $this->getImageselectionPreviewOptions($field_settings);

            $payment_products = array();
            $payment_layout   = 'cards';
            if ((string) $field_item->getProp('field_type') === 'payment') {
                $raw_products = $this->decodeOptions($field_item->getProp('field_options'));
                if (is_array($raw_products)) {
                    foreach ($raw_products as $prod) {
                        if (!is_array($prod)) {
                            continue;
                        }
                        $payment_products[] = array(
                            'name'        => sanitize_text_field((string) ($prod['name'] ?? '')),
                            'price'       => (float) ($prod['price'] ?? 0),
                            'description' => sanitize_textarea_field((string) ($prod['description'] ?? '')),
                            'image'       => esc_url_raw((string) ($prod['image'] ?? '')),
                        );
                    }
                }
                if (is_array($field_settings) && !empty($field_settings['layout'])) {
                    $lv = sanitize_key((string) $field_settings['layout']);
                    if (in_array($lv, array('cards', 'list'), true)) {
                        $payment_layout = $lv;
                    }
                }
            }

            $column_payload['fields'][] = array(
                'id'                   => absint($field_id),
                'label'                => sanitize_text_field($label),
                'description'          => sanitize_textarea_field((string) $field_item->getProp('description')),
                'field_type'           => sanitize_key((string) $field_item->getProp('field_type')),
                'placeholder'          => sanitize_text_field((string) $field_item->getProp('placeholder')),
                'required'             => (bool) $field_item->getProp('is_required'),
                'address_labels'       => $address_labels,
                'line2_enabled'        => $line2_enabled,
                'terms'                => $terms,
                'options'              => $options,
                'phone_display_mode'   => $phone_display_mode,
                'pad_height'           => $pad_height,
                'signature_placeholder' => $signature_placeholder,
                'signature_bg_color'   => $signature_bg_color,
                'signature_pen_color'  => $signature_pen_color,
                'rating_scale'         => $rating_scale,
                'rating_icon_group'    => $rating_icon_group,
                'rating_icon_size'     => $rating_icon_size,
                'rating_color_filled'  => $rating_color_filled,
                'rating_color_empty'   => $rating_color_empty,
                'rating_step_labels'   => $rating_step_labels,
                'rating_custom_image_url' => $rating_custom_image_url,
                'rating_step_image_urls' => $rating_step_image_urls,
                'rating_allow_clear'      => $rating_allow_clear,
                'richtext_toolbar'        => $richtext_toolbar,
                'richtext_placeholder'    => $richtext_placeholder,
                'richtext_min_height'     => $richtext_min_height,
                'likert_rows'             => $likert_rows,
                'likert_columns'          => $likert_columns,
                'likert_allow_na'         => $likert_allow_na,
                'likert_column_width'     => $likert_column_width,
                'imagesel_columns_desktop' => $imagesel_columns_desktop,
                'imagesel_columns_mobile'  => $imagesel_columns_mobile,
                'imagesel_show_title'      => $imagesel_show_title,
                'imagesel_show_description' => $imagesel_show_description,
                'imagesel_options'         => $imagesel_options,
                'payment_products'         => $payment_products,
                'payment_layout'           => $payment_layout,
            );

            $this->field_count++;
        }
    }

    /**
     * Sanitize and normalize field options for select/radio/checkbox.
     */
    private function sanitizeOptions(string $field_type, $raw_options): array
    {
        $raw_options = $this->decodeOptions($raw_options);

        if (!is_array($raw_options) || empty($raw_options)) {
            return array();
        }

        $normalized = array();
        $type = strtolower($field_type);

        if ($type === 'selectfield') {
            foreach ($raw_options as $group => $options) {
                if (!is_array($options)) {
                    continue;
                }
                $group_label = is_string($group) ? sanitize_text_field($group) : '';
                $group_options = array();
                foreach ($options as $text => $value) {
                    $group_options[] = array(
                        'label' => sanitize_text_field((string) $text),
                        'value' => sanitize_text_field((string) $value),
                    );
                }
                $normalized[] = array(
                    'group'   => $group_label,
                    'options' => $group_options,
                );
            }
        } elseif ($type === 'inputradio' || $type === 'inputcheckbox') {
            foreach ($raw_options as $option_group) {
                if (!is_array($option_group)) {
                    continue;
                }
                $group_options = array();
                foreach ($option_group as $text => $value) {
                    $group_options[] = array(
                        'label' => sanitize_text_field((string) $text),
                        'value' => sanitize_text_field((string) $value),
                    );
                }
                if (!empty($group_options)) {
                    $normalized[] = array(
                        'group'   => '',
                        'options' => $group_options,
                    );
                }
            }
        }

        return $normalized;
    }

    /**
     * Build scoped theme CSS for the block preview using FormTheme getters.
     */
    private function buildThemeCss(int $theme_id, array $progress_bar = array()): string
    {
        if ($theme_id < 1) {
            return '';
        }

        $form_theme = new \GriffinForms\Items\FormTheme();
        if (!$form_theme->loadItem($theme_id)) {
            return '';
        }

        $rules = array();

        $form_css = $form_theme->getFormStyleCss();
        if ($form_css !== '') {
            $rules[] = '.griffinforms-block-preview { ' . $form_css . ' }';
        }

        $page_css = $form_theme->getPageStyleCss();
        if ($page_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-page { ' . $page_css . ' }';
        }

        $row_css = $form_theme->getRowStyleCss();
        if ($row_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-row { ' . $row_css . ' }';
        }

        $heading_css = $form_theme->getFormTitleCss();
        if ($heading_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-heading { ' . $heading_css . ' }';
        }

        $desc_css = $form_theme->getFormDescriptionCss();
        if ($desc_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-desc { ' . $desc_css . ' }';
        }

        $page_heading_css = $form_theme->getPageHeadingCss();
        if ($page_heading_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-page-title { ' . $page_heading_css . ' }';
        }

        $page_desc_css = $form_theme->getPageDescriptionCss();
        if ($page_desc_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-page-desc { ' . $page_desc_css . ' }';
        }

        $row_heading_css = $form_theme->getRowHeadingCss();
        if ($row_heading_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-row-heading { ' . $row_heading_css . ' }';
        }

        $row_desc_css = $form_theme->getRowDescriptionCss();
        if ($row_desc_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-row-desc { ' . $row_desc_css . ' }';
        }

        $label_css = $form_theme->getFieldLabelCss();
        if ($label_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-label { ' . $label_css . ' }';
        }

        $field_desc_css = $form_theme->getFieldDescriptionCss();
        if ($field_desc_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-description { ' . $field_desc_css . ' }';
        }

        $style_types = array('text_input', 'textarea', 'select_input', 'checkbox_radio', 'choice');
        foreach ($style_types as $style_type) {
            $selector = '.griffinforms-block-preview [data-gf-style-group="block-preview"][data-gf-style-type="' . esc_attr($style_type) . '"]';

            $default_css = $form_theme->getFieldStyleCss($style_type, 'default');
            if ($default_css !== '') {
                $rules[] = $selector . ' { ' . $default_css . ' }';
            }

            $hover_css = $form_theme->getFieldStyleCss($style_type, 'hover');
            if ($hover_css !== '') {
                $rules[] = $selector . ':hover { ' . $hover_css . ' }';
            }

            $focus_css = $form_theme->getFieldStyleCss($style_type, 'focus');
            if ($focus_css !== '') {
                $rules[] = $selector . ':focus, ' . $selector . ':focus-visible { ' . $focus_css . ' }';
            }
        }

        $choice_border = $form_theme->getFieldStyleResolved('choice', 'default');
        if (!empty($choice_border['border']['color'])) {
            $rules[] = '.griffinforms-block-preview [data-gf-field-type="imageselection"] .gf-imgsel-card-body { border-top-color: ' . esc_attr($choice_border['border']['color']) . ' !important; }';
        }

        $text_input_style = $form_theme->getFieldStyleResolved('text_input', 'default');
        $card_shell_props = array();
        $card_border_width = $this->extractPreviewBorderWidth($text_input_style);
        if ($card_border_width !== '') {
            $card_shell_props[] = 'border-width: ' . esc_attr($card_border_width) . ' !important';
        }
        if (!empty($text_input_style['border']['style'])) {
            $card_shell_props[] = 'border-style: ' . esc_attr($text_input_style['border']['style']) . ' !important';
        }
        if (!empty($text_input_style['border']['color'])) {
            $card_shell_props[] = 'border-color: ' . esc_attr($text_input_style['border']['color']) . ' !important';
        }
        if (!empty($text_input_style['border-radius'])) {
            $card_shell_props[] = 'border-radius: ' . esc_attr($text_input_style['border-radius']) . ' !important';
        }
        if (!empty($card_shell_props)) {
            $rules[] = '.griffinforms-block-preview [data-gf-field-type="imageselection"] .gf-imgsel-card { ' . implode('; ', $card_shell_props) . '; }';
        }
        if (!empty($text_input_style['background-color'])) {
            $rules[] = '.griffinforms-block-preview [data-gf-field-type="imageselection"] .gf-imgsel-card-body { background-color: ' . esc_attr($text_input_style['background-color']) . ' !important; }';
        }

        $imgsel_accent = $form_theme->getSubmitButtonBackgroundColor();
        if (!empty($imgsel_accent)) {
            $rules[] = '.griffinforms-block-preview [data-gf-field-type="imageselection"] .gf-imgsel-card.is-selected { position: relative !important; z-index: 1 !important; border-color: ' . esc_attr($imgsel_accent) . ' !important; box-shadow: none !important; }';
        }

        $prev_css = $form_theme->getPreviousButtonCss('default');
        if ($prev_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-prev, .griffinforms-block-preview [data-gf-button-type="previous"] { ' . $prev_css . ' }';
        }
        $prev_hover = $form_theme->getPreviousButtonCss('hover');
        if ($prev_hover !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-prev:hover, .griffinforms-block-preview [data-gf-button-type="previous"]:hover { ' . $prev_hover . ' }';
        }
        $prev_active = $form_theme->getPreviousButtonCss('active');
        if ($prev_active !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-prev:active, .griffinforms-block-preview [data-gf-button-type="previous"]:active { ' . $prev_active . ' }';
        }

        $next_css = $form_theme->getNextButtonCss('default');
        if ($next_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-next { ' . $next_css . ' }';
        }
        $next_hover = $form_theme->getNextButtonCss('hover');
        if ($next_hover !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-next:hover { ' . $next_hover . ' }';
        }
        $next_active = $form_theme->getNextButtonCss('active');
        if ($next_active !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-next:active { ' . $next_active . ' }';
        }

        $submit_css = $form_theme->getSubmitButtonCss('default');
        if ($submit_css !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-submit { ' . $submit_css . ' }';
        }
        $submit_hover = $form_theme->getSubmitButtonCss('hover');
        if ($submit_hover !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-submit:hover { ' . $submit_hover . ' }';
        }
        $submit_active = $form_theme->getSubmitButtonCss('active');
        if ($submit_active !== '') {
            $rules[] = '.griffinforms-block-preview .gf-bp-nav-submit:active { ' . $submit_active . ' }';
        }

        $label_color = $form_theme->getLabelColor();
        if (!empty($label_color)) {
            $rules[] = '.griffinforms-block-preview .gf-bp-options span:last-child { color: ' . esc_attr($label_color) . ' !important; }';
            $rules[] = '.griffinforms-block-preview .gf-bp-terms-subtext { color: ' . esc_attr($label_color) . ' !important; }';
            $rules[] = '.griffinforms-block-preview .gf-bp-terms-link a { color: ' . esc_attr($label_color) . ' !important; }';
            $rules[] = '.griffinforms-block-preview .gf-bp-terms-link span { color: ' . esc_attr($label_color) . ' !important; }';
            $rules[] = '.griffinforms-block-preview .gf-rating-label { color: ' . esc_attr($label_color) . ' !important; }';
        }

        if (!empty($progress_bar['toggle'])) {
            $accent = $form_theme->getSubmitButtonBackgroundColor();
            if (!empty($accent)) {
                $rules[] = '.griffinforms-block-preview { --gf-progress-bar-accent: ' . esc_attr($accent) . '; }';
            }
        }

        if (empty($rules)) {
            return '';
        }

        $css = implode(' ', $rules);
        return \GriffinForms\Items\FormTheme::prepareStyleTagContent($css);
    }

    /**
     * Fetch address sub-labels from field settings with sensible fallbacks.
     */
    private function getAddressLabels(\GriffinForms\Items\Field $field_item): array
    {
        $defaults = array(
            'line1'   => __('Address Line 1', 'griffinforms-form-builder'),
            'line2'   => __('Address Line 2', 'griffinforms-form-builder'),
            'city'    => __('City', 'griffinforms-form-builder'),
            'state'   => __('State / Province', 'griffinforms-form-builder'),
            'postal'  => __('Postal Code', 'griffinforms-form-builder'),
            'country' => __('Country', 'griffinforms-form-builder'),
        );

        $settings = $field_item->getProp('field_settings');
        $labels = isset($settings['labels']) && is_array($settings['labels']) ? $settings['labels'] : array();

        foreach ($defaults as $key => $fallback) {
            if (isset($labels[$key])) {
                $value = trim((string) $labels[$key]);
                $defaults[$key] = $value === '' ? $fallback : sanitize_text_field($value);
            }
        }

        return $defaults;
    }

    /**
     * Determine if Address Line 2 is enabled.
     */
    private function getAddressLine2Enabled(\GriffinForms\Items\Field $field_item): bool
    {
        $settings = $field_item->getProp('field_settings');
        if (!is_array($settings)) {
            return true;
        }
        if (isset($settings['enable_line2'])) {
            return (int) $settings['enable_line2'] === 1;
        }
        return true;
    }

    /**
     * Convenience to load base style strings for inline use.
     */
    private function addBaseStyles(array &$data, int $theme_id): void
    {
        if ($theme_id < 1) {
            return;
        }
        $form_theme = new \GriffinForms\Items\FormTheme();
        if (!$form_theme->loadItem($theme_id)) {
            return;
        }
        $data['form_style_css'] = $form_theme->getFormStyleCss();
        $data['page_style_css'] = $form_theme->getPageStyleCss();
        $data['row_style_css'] = $form_theme->getRowStyleCss();
        $data['signature_canvas_style'] = $this->buildSignatureCanvasStyle($form_theme);
        $data['richtext_editor_style']  = $this->buildRichtextEditorStyle($form_theme);
        $data['likert_table_style']     = $this->buildLikertTableStyle($form_theme);
    }

    /**
     * Build an inline CSS string for the signature canvas in the block preview.
     * Inherits border, border-radius, and box-shadow from the text_input theme style.
     */
    private function buildSignatureCanvasStyle(\GriffinForms\Items\FormTheme $form_theme): string
    {
        $css = $form_theme->getFieldStyleCss('text_input', 'default');
        if (empty($css)) {
            return '';
        }

        $props = array('border', 'border-color', 'border-width', 'border-style', 'border-radius', 'box-shadow');
        $result = '';
        foreach ($props as $prop) {
            if (preg_match('/' . preg_quote($prop, '/') . '\s*:\s*([^;]+);/', $css, $m)) {
                $result .= $prop . ':' . trim($m[1]) . ';';
            }
        }
        return $result;
    }

    /**
     * Build an inline CSS string for the rich text editor area in the block preview.
     * Inherits border, border-radius, and box-shadow from the text_input theme style.
     */
    private function buildRichtextEditorStyle(\GriffinForms\Items\FormTheme $form_theme): string
    {
        $css = $form_theme->getFieldStyleCss('text_input', 'default');
        if (empty($css)) {
            return '';
        }

        $props = array('border', 'border-color', 'border-width', 'border-style', 'border-radius', 'box-shadow', 'background', 'background-color');
        $result = '';
        foreach ($props as $prop) {
            if (preg_match('/' . preg_quote($prop, '/') . '\s*:\s*([^;]+);/', $css, $m)) {
                $result .= $prop . ':' . trim($m[1]) . ';';
            }
        }
        return $result;
    }

    /**
     * Build inline CSS strings for the Likert table in the block preview.
     * Returns an array: wrapper_style (border + radius for outer div), border_color (for th/td rules).
     */
    private function buildLikertTableStyle(\GriffinForms\Items\FormTheme $form_theme): array
    {
        $css = $form_theme->getFieldStyleCss('text_input', 'default');
        if (empty($css)) {
            return ['wrapper' => '', 'border_color' => ''];
        }

        $wrapper_props = array('border', 'border-color', 'border-width', 'border-style', 'border-radius', 'box-shadow');
        $wrapper = '';
        foreach ($wrapper_props as $prop) {
            if (preg_match('/' . preg_quote($prop, '/') . '\s*:\s*([^;]+);/', $css, $m)) {
                $wrapper .= $prop . ':' . trim($m[1]) . ';';
            }
        }

        $border_color = '';
        if (preg_match('/border-color\s*:\s*([^;]+);/', $css, $m)) {
            $border_color = trim($m[1]);
        } elseif (preg_match('/border\s*:[^;]*\s(#[0-9a-fA-F]{3,8}|rgb[^;]+);/', $css, $m)) {
            $border_color = trim($m[1]);
        }

        $label_color = $form_theme->getLabelColor();

        $bg_color = '';
        if (preg_match('/background-color\s*:\s*([^;]+);/', $css, $m)) {
            $bg_color = trim($m[1]);
        } elseif (preg_match('/background\s*:\s*([^;]+);/', $css, $m)) {
            $bg_color = trim($m[1]);
        }

        return ['wrapper' => $wrapper, 'border_color' => $border_color, 'label_color' => $label_color, 'bg_color' => $bg_color];
    }

    /**
     * Decode options that may be JSON or serialized.
     */
    private function decodeOptions($raw)
    {
        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                return $decoded;
            }
            $unserialized = maybe_unserialize($raw);
            if (is_array($unserialized)) {
                return $unserialized;
            }
        }
        return $raw;
    }

    /**
     * Return external font families for the assigned theme.
     */
    private function getThemeFonts(int $theme_id): array
    {
        if ($theme_id < 1) {
            return array();
        }

        $form_theme = new \GriffinForms\Items\FormTheme();
        if (!$form_theme->loadItem($theme_id)) {
            return array();
        }

        $fonts = $form_theme->getExternalFontFamilies();
        if (!is_array($fonts)) {
            return array();
        }

        $fonts = array_map('trim', $fonts);
        $fonts = array_values(array_filter($fonts));
        return array_map('sanitize_text_field', $fonts);
    }

    /**
     * Extract basic Terms & Conditions settings for preview.
     */
    private function getTermsSettings(\GriffinForms\Items\Field $field_item): array
    {
        $settings = $field_item->getProp('field_settings');
        if (!is_array($settings)) {
            $settings = array();
        }

        $checkbox_label = isset($settings['checkbox_label']) && $settings['checkbox_label'] !== ''
            ? sanitize_text_field((string) $settings['checkbox_label'])
            : __('I agree to the terms', 'griffinforms-form-builder');

        $anchor_text = isset($settings['anchor_text']) && $settings['anchor_text'] !== ''
            ? sanitize_text_field((string) $settings['anchor_text'])
            : __('View terms', 'griffinforms-form-builder');

        $subtext = isset($settings['subtext']) ? sanitize_textarea_field((string) $settings['subtext']) : '';

        $link_type = isset($settings['link_type']) && in_array($settings['link_type'], array('page', 'post'), true)
            ? $settings['link_type']
            : 'page';

        $page_id = isset($settings['page_id']) ? absint($settings['page_id']) : 0;
        $post_id = isset($settings['post_id']) ? absint($settings['post_id']) : 0;

        $target_id = $link_type === 'post' ? $post_id : $page_id;
        $target_url = $target_id ? get_permalink($target_id) : '';

        return array(
            'checkbox_label' => $checkbox_label,
            'anchor_text'    => $anchor_text,
            'subtext'        => $subtext,
            'url'            => esc_url_raw($target_url),
        );
    }

    /**
     * Extract a compact Image Selection preview payload for Gutenberg.
     */
    private function getImageselectionPreviewOptions($field_settings): array
    {
        if (!is_array($field_settings) || empty($field_settings['options']) || !is_array($field_settings['options'])) {
            return array();
        }

        $options = array();

        foreach ($field_settings['options'] as $option) {
            if (!is_array($option)) {
                continue;
            }

            $options[] = array(
                'image_url'    => !empty($option['image_url']) ? esc_url_raw((string) $option['image_url']) : '',
                'title'        => !empty($option['title']) ? sanitize_text_field((string) $option['title']) : '',
                'description'  => !empty($option['description']) ? sanitize_textarea_field((string) $option['description']) : '',
            );
        }

        return $options;
    }

    /**
     * Extract a usable preview border width from a resolved style array.
     */
    private function extractPreviewBorderWidth(array $style): string
    {
        if (empty($style['border']['width'])) {
            return '';
        }

        $width = $style['border']['width'];
        if (is_array($width)) {
            foreach (array('top', 'right', 'bottom', 'left') as $side) {
                if (!empty($width[$side]) && is_string($width[$side])) {
                    return $width[$side];
                }
            }
            return '';
        }

        return is_string($width) ? $width : '';
    }
}
