<?php

namespace GriffinForms\Admin\Html\Pages\Single;

use GriffinForms\Includes\Helpers\Currency;
use GriffinForms\Frontend\Security\FormGuardManager;
use GriffinForms\Includes\Integrations\Providers;
use GriffinForms\Includes\Onboarding\PlacementService;
use GriffinForms\Includes\Pipelines\SubmissionContextBuilder;
use GriffinForms\Includes\Pipelines\SubmissionHistoryManager;

class Submission extends \GriffinForms\Admin\Html\Pages\Single\Format
{   
    protected $type = 'submission';
    protected $meta;
    protected $config;
    protected $lang;
    protected $sql;
    protected $js;
    protected $item_id;
    protected $item;
    protected $is_valid_form;
    protected $form;
    protected $formpages;
    protected $submission_field_values = [];
    protected $cart = [];
    protected $decrypt_nonce;
    protected $payment_status = '';
    protected $payment_meta = [];
    protected $submission_fields = [];
    protected $field_cache = [];
    protected $is_payment_form = false;
    protected $submission_meta_payload = null;
    protected $accounts_email_field_ids = null;
    protected $revision_list_cache = null;
    protected $upsell_registry;
    protected $marketing_activity_cards = null;
    
    protected function setIsValidForm()
    {
        $this->is_valid_form = $this->sql->itemExists('form', $this->item->getProp('form_id'));
    }

    protected function setForm()
    {
        if ($this->is_valid_form) {
            $this->form = new \GriffinForms\Items\Form();
            $this->form->loadItem($this->item->getProp('form_id'));
        }
    }

    protected function setFormPages()
    {
        $submission = $this->item->getProp('submission');
        $this->formpages = is_array($submission) ? array_keys($submission) : [];
    }

    protected function setUpsellRegistry()
    {
        $this->upsell_registry = \GriffinForms\Includes\Upsells\UpsellRegistry::getInstance();
    }

    protected function canAccessSubmissionEditing(): bool
    {
        return $this->upsell_registry && $this->upsell_registry->isFeatureAdmitted('submission_editing');
    }

    protected function canAccessSubmissionExport(): bool
    {
        return $this->upsell_registry && $this->upsell_registry->isFeatureAdmitted('submission_export');
    }

    protected function submissionFormPages()
    {
        $page_ids = [];
        $submission = $this->item->getProp('submission');
        if (!is_array($submission) || empty($submission)) {
            return $page_ids;
        }

        $submission_formpages = array_keys($submission);
        foreach ($submission_formpages as $formpage) {
            $page_ids[] = str_replace('page_', '', $formpage);
        }
        return $page_ids;
    }

    protected function setSubmissionFieldValues()
    {
        $submission_data = $this->item->getProp('submission');
        $this->submission_field_values = [];
        $this->submission_fields = [];

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

        foreach ($submission_data as $page_id => $page_field_values) {
            if (!is_array($page_field_values)) {
                continue;
            }
            foreach ($page_field_values as $field_id => $field_values) {
                $field_id = absint(str_replace('field_', '', (string) $field_id));
                if ($field_id <= 0) {
                    continue;
                }
                $normalized = $this->normalizeFieldValue($field_values);
                $this->submission_field_values['field_' . $field_id] = $normalized;
                $this->submission_field_values[$field_id] = $normalized;
                $this->submission_fields[] = array(
                    'id' => $field_id,
                    'values' => $normalized,
                    'page' => $this->normalizePageKey($page_id),
                );
            }
        }
    }

    protected function normalizeFieldValue($field_values)
    {
        if (is_array($field_values)) {
            return $field_values;
        }

        if ($field_values === null) {
            return array('');
        }

        return array($field_values);
    }

    protected function normalizePageKey($page_key): string
    {
        $page_key = (string) $page_key;
        if ($page_key === '') {
            return '';
        }

        return trim(str_replace('page_', '', $page_key));
    }

    protected function setCart()
    {
        if (!$this->is_payment_form) {
            $this->cart = [];
            return;
        }
        $form_id = (int) $this->item->getProp('form_id');
        $submission_data = $this->item->getProp('submission');
        $builder = \GriffinForms\Includes\Payments\CartBuilder::getInstance();
        $this->cart = $builder->build($form_id, is_array($submission_data) ? $submission_data : []);
    }

    protected function setPaymentMeta()
    {
        if (!$this->is_payment_form) {
            $this->payment_status = '';
            $this->payment_meta = [];
            return;
        }
        $raw_status = $this->item->getProp('payment_status');
        $this->payment_status = sanitize_text_field(is_string($raw_status) ? $raw_status : '');
        $raw_meta = $this->item->getProp('payment_meta');
        if (is_string($raw_meta)) {
            $decoded = json_decode($raw_meta, true);
            $this->payment_meta = is_array($decoded) ? $decoded : [];
        } elseif (is_array($raw_meta)) {
            $this->payment_meta = $raw_meta;
        } else {
            $this->payment_meta = [];
        }

        if ($this->payment_status === '' && !empty($this->payment_meta)) {
            $this->payment_status = 'pending';
        }
    }

    protected function setIspaymentform()
    {
        $flag = $this->item->getProp('is_payment_form');
        $this->is_payment_form = (bool) (is_string($flag) ? (int) $flag : $flag);
    }

    public function itemOptions()
    {

    }

    protected function setJsData()
    {
        $this->js_data = array(
            'item_type' => strtolower($this->meta),
            'item_id' => $this->item_id,
            'referer' => $this->referer,
            'mark_paid' => array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'action' => 'griffinforms_mark_payment_paid',
                'nonce' => wp_create_nonce('griffinforms_mark_payment_paid'),
                'submission_id' => $this->item_id,
                'strings' => array(
                    'success' => __('Payment marked as paid.', 'griffinforms-form-builder'),
                    'error' => __('Unable to mark payment as paid.', 'griffinforms-form-builder'),
                ),
            ),
            'mark_unpaid' => array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'action' => 'griffinforms_mark_payment_unpaid',
                'nonce' => wp_create_nonce('griffinforms_mark_payment_unpaid'),
                'submission_id' => $this->item_id,
                'strings' => array(
                    'success' => __('Payment marked as unpaid.', 'griffinforms-form-builder'),
                    'error' => __('Unable to mark payment as unpaid.', 'griffinforms-form-builder'),
                ),
            ),
            'delete_submission' => array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'action' => 'deleteItem',
                'nonce' => wp_create_nonce('delete_item'),
                'submission_id' => $this->item_id,
                'redirect' => admin_url('admin.php?page=griffinforms-submissions'),
                'strings' => array(
                    'confirm' => __('Are you sure you want to delete this submission?', 'griffinforms-form-builder'),
                    'success' => __('Submission deleted.', 'griffinforms-form-builder'),
                    'error' => __('Unable to delete submission.', 'griffinforms-form-builder'),
                ),
            ),
            'accounts' => array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'submission_id' => $this->item_id,
                'get_state_action' => 'griffinforms_get_submission_accounts',
                'get_state_nonce' => wp_create_nonce('griffinforms_get_submission_accounts'),
                'validate_action' => 'griffinforms_validate_submission_account_action',
                'validate_nonce' => wp_create_nonce('griffinforms_validate_submission_account_action'),
                'execute_action' => 'griffinforms_execute_submission_account_action',
                'execute_nonce' => wp_create_nonce('griffinforms_execute_submission_account_action'),
                'strings' => array(
                    'title' => __('User Registration', 'griffinforms-form-builder'),
                    'empty' => __('No user registration activity has been recorded for this submission yet.', 'griffinforms-form-builder'),
                    'loading' => __('Loading user registration activity...', 'griffinforms-form-builder'),
                    'error' => __('Unable to load user registration activity for this submission.', 'griffinforms-form-builder'),
                    'action_error' => __('Unable to complete this user registration action.', 'griffinforms-form-builder'),
                    'confirm_prefix' => __('Confirm action:', 'griffinforms-form-builder'),
                    'confirm_title' => __('Confirm this user registration action', 'griffinforms-form-builder'),
                    'confirm_cancel' => __('Cancel', 'griffinforms-form-builder'),
                    'confirm_button' => __('Confirm', 'griffinforms-form-builder'),
                    'working' => __('Working...', 'griffinforms-form-builder'),
                    'confirm_descriptions' => array(
                        'create_now' => __('This will create the user now.', 'griffinforms-form-builder'),
                        'activate_pending' => __('This will approve this sign-up and continue user creation.', 'griffinforms-form-builder'),
                        'retry_failed' => __('This will retry the previously failed user registration action.', 'griffinforms-form-builder'),
                        'send_confirmation_now' => __('This will send the confirmation email now.', 'griffinforms-form-builder'),
                        'mark_no_action' => __('This will mark this user registration action as no action.', 'griffinforms-form-builder'),
                    ),
                    'logs_hint' => __('Detailed history appears in Logs below.', 'griffinforms-form-builder'),
                ),
            ),
            'history' => array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'submission_id' => $this->item_id,
                'restore_action' => 'griffinforms_restore_submission_revision',
                'restore_nonce' => wp_create_nonce('restore_submission_revision'),
                'preview_action' => 'griffinforms_preview_submission_revision',
                'preview_nonce' => wp_create_nonce('preview_submission_revision'),
                'strings' => array(
                    'title' => __('Submission history', 'griffinforms-form-builder'),
                    'empty' => __('No submission revisions have been recorded yet.', 'griffinforms-form-builder'),
                    'loading' => __('Loading...', 'griffinforms-form-builder'),
                    'preview_loading' => __('Loading preview...', 'griffinforms-form-builder'),
                    'preview_error' => __('Unable to load the snapshot preview.', 'griffinforms-form-builder'),
                    'confirm_restore' => __('Restore this revision?', 'griffinforms-form-builder'),
                    'restore_success' => __('Submission restored.', 'griffinforms-form-builder'),
                    'restore_error' => __('Unable to restore the selected snapshot.', 'griffinforms-form-builder'),
                ),
            ),
            'compare' => array(
                'ajax_url'       => admin_url('admin-ajax.php'),
                'submission_id'  => $this->item_id,
                'preview_action' => 'griffinforms_preview_submission_revision',
                'preview_nonce'  => wp_create_nonce('preview_submission_revision'),
                'revisions'      => $this->getRevisionListForJs((int) $this->item_id),
                'strings'        => array(
                    'loading'       => __('Loading...', 'griffinforms-form-builder'),
                    'error'         => __('Unable to load revision.', 'griffinforms-form-builder'),
                    'no_data'       => __('No field data in this revision.', 'griffinforms-form-builder'),
                    'empty_prompt'  => __('Select a revision in each column to compare.', 'griffinforms-form-builder'),
                    'field_removed' => __('Field removed', 'griffinforms-form-builder'),
                ),
            ),
        );
    }

    /**
     * Render the back button for submission page.
     */
    protected function backToListBtn()
    {
        $this->buttons->bsBtn([
            'class' => 'gf-back-to-submissions-btn gf-btn-light mb-3',
            'label' => $this->lang->getText('Back to submissions'),
            'link'  => admin_url('admin.php?page=griffinforms-submissions'),
            'icon'  => 'list'
        ]);
    }

    public function html()
    {
        wp_add_inline_script(
            'griffinforms-submission',
            'window.griffinformsPasswordStrings = ' . wp_json_encode(array(
                'showLabel' => __('Show', 'griffinforms-form-builder'),
                'hideLabel' => __('Hide', 'griffinforms-form-builder'),
                'loading' => __('Decrypting...', 'griffinforms-form-builder'),
                'error' => __('Unable to reveal password.', 'griffinforms-form-builder')
            )) . ';',
            'before'
        );

        if ($this->item_id <= 0 || !$this->sql->itemExists('submission', $this->item_id)) {
            echo '<div class="griffinforms-single-submission container-fluid mt-4">';
            $this->renderMissingSubmissionNotice();
            echo '</div>';
            return;
        }

        $this->markSubmissionAsRead();

        $this->setCart();
        $this->setPaymentMeta();

        echo '<div class="griffinforms-single-submission container-fluid mt-4">';
        echo '<div class="mb-3">';
        $this->topNav();
        echo '</div>';

        echo '<div class="gf-submission-page-header d-flex flex-wrap justify-content-between align-items-center mb-4">';
        echo '<div>';
        $this->getHeadingHtml();
        echo '<div class="text-muted small">';
        printf(esc_html__('Submission #%d', 'griffinforms-form-builder'), absint($this->item_id));
        echo '</div>';
        echo '</div>';
        echo '</div>';

        $resume_url = $this->getResumeUrl();
        $submission_edit_button = $this->getSubmissionEditButtonState();
        echo '<div class="gf-submission-actions-strip d-flex flex-wrap align-items-center gap-2 justify-content-start mb-3">';
        if ($this->is_payment_form && $this->payment_status === 'pending' && !empty($resume_url)) {
            echo '<a class="button button-primary" href="' . esc_url($resume_url) . '" target="_blank" rel="noopener">' . esc_html__('Resume payment', 'griffinforms-form-builder') . '</a>';
        }
        if ($this->is_payment_form && current_user_can('manage_options') && $this->payment_status === 'pending') {
            echo '<button type="button" id="gf-mark-paid-btn" class="button button-primary">';
            echo '<span class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>';
            echo esc_html__('Mark as Paid', 'griffinforms-form-builder');
            echo '</button>';
        }
        if ($this->is_payment_form && current_user_can('manage_options') && $this->payment_status === 'paid') {
            echo '<button type="button" id="gf-mark-unpaid-btn" class="button">';
            echo '<span class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>';
            echo esc_html__('Mark as Unpaid', 'griffinforms-form-builder');
            echo '</button>';
        }
        if (current_user_can('manage_options')) {
            if (!empty($submission_edit_button['allowed']) && !empty($submission_edit_button['url'])) {
                echo '<a class="button button-secondary" href="' . esc_url($submission_edit_button['url']) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Edit submission', 'griffinforms-form-builder') . '</a>';
            } else {
                $disabled_reason = isset($submission_edit_button['message']) ? (string) $submission_edit_button['message'] : __('Submission editing is not available right now.', 'griffinforms-form-builder');
                echo '<button type="button" class="button button-secondary" disabled aria-disabled="true" title="' . esc_attr($disabled_reason) . '">' . esc_html__('Edit submission', 'griffinforms-form-builder') . '</button>';
            }
            echo '<button type="button" id="gf-delete-submission-btn" class="button">';
            echo '<span class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>';
            echo esc_html__('Delete submission', 'griffinforms-form-builder');
            echo '</button>';
        }
        if (current_user_can('manage_options') && !$this->canAccessSubmissionExport() && $this->upsell_registry && $this->upsell_registry->shouldRenderLockedShell('submission_export')) {
            echo '<button type="button" class="button" disabled aria-disabled="true" title="' . esc_attr__('Submission download is not available right now.', 'griffinforms-form-builder') . '">' . esc_html__('Download', 'griffinforms-form-builder') . '</button>';
        }

        /**
         * Hook: submission actions strip for buttons/links.
         * Use this to render buttons related to the submission (e.g., resend emails, export).
         */
        do_action('griffinforms_submission_actions_strip', $this->item_id);
        if (current_user_can('manage_options')) {
            $meta = $this->getSubmissionMetaPayload();
            if (!empty($meta['has_data']) && isset($meta['compliance_mode']) && (int) $meta['compliance_mode'] === 1) {
                $download_url = add_query_arg(
                    array(
                        'action' => 'griffinforms_download_compliance_meta',
                        'submission_id' => absint($this->item_id),
                        'nonce' => wp_create_nonce('griffinforms_download_compliance_meta'),
                    ),
                    admin_url('admin-ajax.php')
                );
                echo '<a class="button button-secondary ms-auto" href="' . esc_url($download_url) . '">';
                echo esc_html__('Download compliance data', 'griffinforms-form-builder');
                echo '</a>';
            }
        }
        echo '</div>';

        echo '<div class="row gx-4 gy-4 gf-submission-layout">';
        echo '<div class="col-12 col-xxl-3 order-2 order-xxl-1">';
        $this->renderMetaSidebar();
        echo '</div>';
        echo '<div class="col-12 col-xxl-6 order-1 order-xxl-2">';
        $this->renderMainColumn();
        echo '</div>';
        echo '<div class="col-12 col-xxl-3 order-3">';
        $this->renderRightSidebar();
        echo '</div>';
        echo '</div>';

        $this->renderHistoryPreviewModal();
        $this->renderCompareModal();

        echo '</div>';
    }

    private function renderHistoryPreviewModal(): void
    {
        $modal_id = 'gf-revision-preview-modal';
        echo '<div class="modal fade" id="' . esc_attr($modal_id) . '" tabindex="-1" aria-labelledby="' . esc_attr($modal_id) . '-label" aria-hidden="true" data-gf-revision-preview-modal="1">';
        echo '<div class="modal-dialog modal-lg modal-dialog-scrollable">';
        echo '<div class="modal-content">';
        echo '<div class="modal-header">';
        echo '<div>';
        echo '<h5 class="modal-title mb-0" id="' . esc_attr($modal_id) . '-label">' . esc_html__('Snapshot preview', 'griffinforms-form-builder') . '</h5>';
        echo '<div class="small text-muted mt-1" data-gf-revision-preview-meta></div>';
        echo '</div>';
        echo '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="' . esc_attr__('Close', 'griffinforms-form-builder') . '"></button>';
        echo '</div>';
        echo '<div class="modal-body" data-gf-revision-preview-body>';
        echo '<div class="text-center text-muted py-4" data-gf-revision-preview-loading>';
        echo '<div class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></div>';
        echo esc_html__('Loading preview...', 'griffinforms-form-builder');
        echo '</div>';
        echo '</div>';
        echo '<div class="modal-footer gap-2">';
        echo '<button type="button" class="button" data-bs-dismiss="modal">' . esc_html__('Close', 'griffinforms-form-builder') . '</button>';
        echo '<button type="button" class="button button-primary" data-gf-revision-preview-restore-btn="1" disabled>' . esc_html__('Restore this snapshot', 'griffinforms-form-builder') . '</button>';
        echo '</div>';
        echo '</div>';
        echo '</div>';
        echo '</div>';
    }

    private function renderCompareModal(): void
    {
        $modal_id = 'gf-revision-compare-modal';
        echo '<div class="modal fade" id="' . esc_attr($modal_id) . '" tabindex="-1" aria-labelledby="' . esc_attr($modal_id) . '-label" aria-hidden="true" data-bs-backdrop="static" data-gf-revision-compare-modal="1">';
        echo '<div class="modal-dialog modal-fullscreen">';
        echo '<div class="modal-content">';

        echo '<div class="modal-header">';
        echo '<h5 class="modal-title" id="' . esc_attr($modal_id) . '-label">' . esc_html__('Compare revisions', 'griffinforms-form-builder') . '</h5>';
        echo '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="' . esc_attr__('Close', 'griffinforms-form-builder') . '"></button>';
        echo '</div>';

        echo '<div class="modal-body p-3">';
        echo '<div class="table-responsive">';
        echo '<table class="table table-bordered align-top gf-revision-compare-table">';
        echo '<thead>';
        echo '<tr>';
        echo '<th class="gf-compare-label-col">' . esc_html__('Field', 'griffinforms-form-builder') . '</th>';
        echo '<th class="gf-compare-col" data-gf-compare-col="a">';
        echo '<select class="form-select form-select-sm gf-compare-revision-select" data-gf-compare-target="a">';
        echo '<option value="">' . esc_html__('Select revision', 'griffinforms-form-builder') . '</option>';
        echo '</select>';
        echo '</th>';
        echo '<th class="gf-compare-col" data-gf-compare-col="b">';
        echo '<select class="form-select form-select-sm gf-compare-revision-select" data-gf-compare-target="b">';
        echo '<option value="">' . esc_html__('Select revision', 'griffinforms-form-builder') . '</option>';
        echo '</select>';
        echo '</th>';
        echo '</tr>';
        echo '</thead>';
        echo '<tbody data-gf-compare-body></tbody>';
        echo '</table>';
        echo '</div>';
        echo '<div class="text-center text-muted py-4" data-gf-compare-empty>' . esc_html__('Select a revision in each column to compare.', 'griffinforms-form-builder') . '</div>';
        echo '</div>';

        echo '<div class="modal-footer">';
        echo '<button type="button" class="button" data-bs-dismiss="modal">' . esc_html__('Close', 'griffinforms-form-builder') . '</button>';
        echo '</div>';

        echo '</div>';
        echo '</div>';
        echo '</div>';
    }

    /**
     * Mark current submission as read when admin opens submission detail page.
     */
    private function markSubmissionAsRead(): void
    {
        global $wpdb;

        $submission_id = absint($this->item_id);
        if ($submission_id <= 0) {
            return;
        }

        $table = $this->config->getTable('submission');
        $wpdb->query(
            $wpdb->prepare(
                "UPDATE %i SET is_read = 1 WHERE id = %d AND is_read <> 1",
                $table,
                $submission_id
            )
        );
    }

    private function getHeadingHtml()
    {
        if ($this->is_valid_form) {
            $this->headingWithFormName();
        } else {
            $this->normalHeading();
        }
    }

    private function headingWithFormName()
    {
        echo '<h4>';
        // Translators: %s is the name of the form being submitted.
        printf(esc_html__('Submission for %s', 'griffinforms-form-builder'), esc_html($this->form->getProp('name')));
        echo '</h4>';
    }

    private function normalHeading()
    {
        echo '<h4>';
        // Translators: %d is the ID of the form being submitted.
        printf(esc_html__('Submission for Form %d', 'griffinforms-form-builder'), esc_html($this->item->getProp('form_id')));
        echo '</h4>';

        echo '<div class="text-secondary small">';
        esc_html_e('Note: This form no longer exists', 'griffinforms-form-builder');
        echo '</div>';
    }

    private function formatDateTime($raw): string
    {
        if (empty($raw)) {
            return '';
        }

        $timestamp = is_numeric($raw) ? (int) $raw : strtotime((string) $raw);
        if (!$timestamp) {
            return '';
        }

        $date_format = get_option('date_format');
        $time_format = get_option('time_format');

        return wp_date("{$date_format} {$time_format}", $timestamp);
    }

    private function resolveUserSummary(): array
    {
        $user_id = $this->item->getProp('author');
        $user = $user_id ? get_user_by('id', $user_id) : null;

        return array(
            'id' => $user_id ? absint($user_id) : 0,
            'name' => $user ? esc_html($user->display_name) : esc_html__('Anonymous User', 'griffinforms-form-builder'),
            'email' => $user ? esc_html($user->user_email) : '',
            'avatar' => $user_id ? get_avatar($user_id, 40, 'gravatar_default') : '',
            'submitted_at' => $this->formatDateTime($this->item->getProp('created')),
        );
    }

    private function getPaymentTimestamp(): string
    {
        $timestamp = 0;

        if (!empty($this->payment_meta['paypal']['updated_at'])) {
            $timestamp = (int) $this->payment_meta['paypal']['updated_at'];
        }

        if (!empty($this->payment_meta['stripe']['updated_at'])) {
            $timestamp = (int) $this->payment_meta['stripe']['updated_at'];
        }

        if (!empty($this->payment_meta['square']['updated_at'])) {
            $timestamp = strtotime((string) $this->payment_meta['square']['updated_at']);
        }

        if ($timestamp <= 0) {
            $last_edited = $this->item->getProp('last_edited');
            if (!empty($last_edited)) {
                $timestamp = strtotime((string) $last_edited);
            }
        }

        if ($timestamp <= 0) {
            return '';
        }

        return $this->formatDateTime($timestamp);
    }

    private function getRevenueSummary(): array
    {
        $currency_helper = Currency::getInstance();
        $cart_currency = $this->cart['currency'] ?? 'USD';
        $cart_total = isset($this->cart['total']) ? (float) $this->cart['total'] : null;

        if ($cart_total === null && !empty($this->payment_meta['paypal']['display_amount'])) {
            $cart_total = (float) $this->payment_meta['paypal']['display_amount'];
        }

        if ($cart_total === null && !empty($this->payment_meta['stripe']['display_amount'])) {
            $cart_total = (float) $this->payment_meta['stripe']['display_amount'];
        }

        if ($cart_total === null && !empty($this->payment_meta['square']['display_amount'])) {
            $cart_total = (float) $this->payment_meta['square']['display_amount'];
        }

        return array(
            'amount' => $cart_total,
            'currency' => $cart_currency,
            'formatted' => $cart_total !== null ? $currency_helper->formatAmount((float) $cart_total, $cart_currency) : '',
        );
    }

    private function resolveGatewayName(): string
    {
        // Pick the most recently updated gateway entry — handles mid-flow gateway switches.
        // PayPal/Stripe store updated_at as a Unix timestamp; Square as a MySQL datetime string.
        $latest = '';
        $latest_ts = 0;
        foreach (['paypal', 'stripe', 'square'] as $gw) {
            if (empty($this->payment_meta[$gw])) {
                continue;
            }
            $raw = $this->payment_meta[$gw]['updated_at'] ?? 0;
            $ts  = is_numeric($raw) ? (int) $raw : (int) strtotime((string) $raw);
            if ($ts > $latest_ts) {
                $latest_ts = $ts;
                $latest    = $gw;
            }
        }

        if ($latest !== '') {
            return $latest;
        }

        if (!empty($this->payment_meta['gateway'])) {
            return sanitize_text_field((string) $this->payment_meta['gateway']);
        }

        return '';
    }

    private function getPaymentProviderMeta(string $gateway): array
    {
        if ($gateway === '') {
            return array();
        }

        $providers = Providers::getInstance();
        $provider = $providers->get(sanitize_key($gateway), true);

        return is_array($provider) ? $provider : array();
    }

    private function renderMissingSubmissionNotice(): void
    {
        $deleted = $this->getDeletionLogMeta();
        echo '<div class="notice notice-error" style="padding:12px 16px;">';
        echo '<p class="mb-1"><strong>' . esc_html__('This submission no longer exists.', 'griffinforms-form-builder') . '</strong></p>';
        if (!empty($deleted)) {
            echo '<p class="mb-0 small text-muted">';
            echo sprintf(
                esc_html__('Deleted by %1$s on %2$s.', 'griffinforms-form-builder'),
                esc_html($deleted['user']),
                esc_html($deleted['time'])
            );
            echo '</p>';
        }
        echo '</div>';
    }

    private function getDeletionLogMeta(): array
    {
        if ($this->item_id <= 0) {
            return array();
        }

        $logs = $this->log->getByItem('submission', $this->item_id);
        if (empty($logs) || !is_array($logs)) {
            return array();
        }

        foreach ($logs as $log) {
            $message = isset($log->message) ? (string) $log->message : '';
            if (stripos($message, 'delete') === false && stripos($message, 'deleted') === false) {
                continue;
            }
            $user = __('Unknown user', 'griffinforms-form-builder');
            if (!empty($log->user_id)) {
                $u = get_user_by('id', (int) $log->user_id);
                if ($u && $u->exists()) {
                    $user = $u->display_name;
                }
            }
            $time = $this->formatDateTime($log->date_time ?? '');
            return array(
                'user' => $user,
                'time' => $time ?: '',
            );
        }

        return array();
    }

    protected function getUserDataHtml()
    {
        // Retrieve the user ID and check if it's valid
        $user_id = $this->item->getProp('author');
        if (!$user_id) {
            echo esc_html__('Submitted by Anonymous User', 'griffinforms-form-builder');
            return;
        }
    
        // Get user data and handle anonymous users
        $user = get_user_by('id', $user_id);
        $user_name = $user ? esc_html($user->display_name) : esc_html__('Anonymous User', 'griffinforms-form-builder');
    
        // Get creation date and format it according to the site settings
        $created = $this->item->getProp('created');
        $date_format = get_option('date_format');
        $time_format = get_option('time_format');
        $date = esc_html(wp_date("{$date_format} {$time_format}", strtotime($created)));
    
        // Prepare avatar and user details for output
        $avatar = get_avatar($user_id, 18, 'gravatar_default');
    
        echo '<span class="me-2 align-text-bottom">' . wp_kses_post($avatar) . '</span>';
        echo '<span class="fw-bold">' . esc_html($user_name) . '</span> ';
        echo esc_html__('on', 'griffinforms-form-builder') . ' ';
        echo esc_html($date);
    }

    private function renderMetaSidebar(): void
    {
        $user = $this->resolveUserSummary();
        $payment_timestamp = $this->getPaymentTimestamp();
        $revenue = $this->getRevenueSummary();

        echo '<div class="gf-submission-card gf-submission-meta bg-white border rounded p-4">';
        echo '<div class="text-uppercase text-muted small fw-semibold mb-1">' . esc_html__('Form', 'griffinforms-form-builder') . '</div>';
        echo '<div class="fw-semibold">';
        if ($this->is_valid_form && $this->form) {
            echo esc_html($this->form->getProp('name'));
        } else {
            printf(esc_html__('Form %d (removed)', 'griffinforms-form-builder'), esc_html($this->item->getProp('form_id')));
        }
        echo '</div>';
        if (!$this->is_valid_form) {
            echo '<div class="text-danger small mt-1">' . esc_html__('This form no longer exists.', 'griffinforms-form-builder') . '</div>';
        }

        echo '<div class="gf-meta-block mt-4 mb-4">';
        echo '<div class="text-uppercase text-muted small fw-semibold mb-2">' . esc_html__('Submitted by', 'griffinforms-form-builder') . '</div>';
        echo '<div class="d-flex align-items-center gap-3">';
        if (!empty($user['avatar'])) {
            echo '<span class="gf-submission-avatar rounded-circle overflow-hidden">' . wp_kses_post($user['avatar']) . '</span>';
        }
        echo '<div class="flex-grow-1">';
        echo '<div class="fw-semibold">' . esc_html($user['name']) . '</div>';
        if (!empty($user['email'])) {
            echo '<div class="small text-muted">' . esc_html($user['email']) . '</div>';
        }
        if (!empty($user['submitted_at'])) {
            echo '<div class="small text-muted">' . sprintf(esc_html__('Submitted %s', 'griffinforms-form-builder'), esc_html($user['submitted_at'])) . '</div>';
        }
        echo '</div></div>';
        echo '</div>';

        echo '<div class="gf-meta-list">';
        echo '<div class="d-flex justify-content-between align-items-center mb-2">';
        echo '<span class="text-muted small">' . esc_html__('Submission time', 'griffinforms-form-builder') . '</span>';
        echo '<span>' . (!empty($user['submitted_at']) ? esc_html($user['submitted_at']) : esc_html__('N/A', 'griffinforms-form-builder')) . '</span>';
        echo '</div>';

        if ($this->is_payment_form) {
            echo '<div class="d-flex justify-content-between align-items-center mb-2">';
            echo '<span class="text-muted small">' . esc_html__('Payment status', 'griffinforms-form-builder') . '</span>';
            echo '<span>' . wp_kses_post($this->paymentStatusBadge($this->payment_status)) . '</span>';
            echo '</div>';

            if (!empty($payment_timestamp)) {
                echo '<div class="d-flex justify-content-between align-items-center mb-2">';
                echo '<span class="text-muted small">' . esc_html__('Payment updated', 'griffinforms-form-builder') . '</span>';
                echo '<span>' . esc_html($payment_timestamp) . '</span>';
                echo '</div>';
            }

            if (!empty($revenue['formatted'])) {
                echo '<div class="d-flex justify-content-between align-items-center mb-2">';
                echo '<span class="text-muted small">' . esc_html__('Revenue collected', 'griffinforms-form-builder') . '</span>';
                echo '<span class="fw-semibold">' . esc_html($revenue['formatted']) . '</span>';
                echo '</div>';
            }
        }

        echo '</div>';
        echo '</div>';
        if ($this->is_payment_form) {
            $this->renderPaymentSummaryBlock();
        }
        $this->renderMarketingSummaryCards();
        $this->renderEditSummaryCard();
    }

    private function renderMarketingSummaryCards(): void
    {
        $cards = $this->getMarketingActivityCards();
        if (empty($cards)) {
            return;
        }

        foreach ($cards as $card) {
            $title = trim((string) ($card['title'] ?? __('Marketing', 'griffinforms-form-builder')));
            $provider_label = trim((string) ($card['provider_label'] ?? ''));
            $provider_icon = trim((string) ($card['provider_icon'] ?? ''));
            $provider_icon_class = trim((string) ($card['provider_icon_class'] ?? ''));
            $summary = trim((string) ($card['summary'] ?? ''));
            $meta = isset($card['meta']) && is_array($card['meta']) ? $card['meta'] : [];
            $badge_html = (string) ($card['badge_html'] ?? '');

            echo '<div class="gf-submission-card gf-marketing-summary border rounded bg-white p-4 mb-4">';
            echo '<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-3">';
            echo '<div>';
            echo '<div class="text-uppercase text-muted small fw-semibold">' . esc_html($title) . '</div>';
            if ($provider_label !== '') {
                echo '<div class="small text-muted">' . esc_html($provider_label) . '</div>';
            }
            echo '</div>';
            echo '<div class="d-flex flex-wrap gap-2 align-items-center">';
            if ($provider_icon !== '') {
                $icon_classes = trim('gf-marketing-summary-icon ' . $provider_icon_class);
                echo '<span class="gf-marketing-summary-icon-wrap"><img src="' . esc_url($provider_icon) . '" alt="' . esc_attr($provider_label !== '' ? $provider_label : $title) . '" class="' . esc_attr($icon_classes) . '"></span>';
            }
            echo wp_kses_post($badge_html);
            echo '</div>';
            echo '</div>';

            if ($summary !== '') {
                echo '<div class="gf-marketing-summary-copy text-muted small mb-3">' . esc_html($summary) . '</div>';
            }

            if (!empty($meta)) {
                echo '<div class="gf-marketing-summary-meta">';
                foreach ($meta as $row) {
                    if (!is_array($row)) {
                        continue;
                    }

                    $label = trim((string) ($row['label'] ?? ''));
                    $value = isset($row['value']) ? trim((string) $row['value']) : '';
                    if ($label === '' || $value === '') {
                        continue;
                    }

                    echo '<div class="d-flex justify-content-between align-items-start gap-3 mb-2">';
                    echo '<span class="text-muted small">' . esc_html($label) . '</span>';
                    echo '<span class="small text-end gf-marketing-summary-meta-value">' . esc_html($value) . '</span>';
                    echo '</div>';
                }
                echo '</div>';
            }

            echo '</div>';
        }
    }

    private function renderEditSummaryCard(): void
    {
        if (!$this->canAccessSubmissionEditing() && $this->upsell_registry->shouldRenderLockedShell('submission_editing')) {
            echo '<div class="gf-submission-card gf-submission-edit-summary bg-white border rounded p-4 mt-4">';
            $this->upsell_registry->render('submission_editing', 'card', [
                'badge' => __('Pro Preview', 'griffinforms-form-builder'),
            ]);
            echo '</div>';
            return;
        }

        $edit_count = absint($this->item->getProp('submission_edit_count'));

        $lines = [];

        if ($edit_count === 0) {
            $lines[] = __('This submission has not been edited.', 'griffinforms-form-builder');
        } elseif ($edit_count === 1) {
            $lines[] = __('This submission has been edited once.', 'griffinforms-form-builder');
        } else {
            /* translators: %d: number of edits */
            $lines[] = sprintf(__('This submission has been edited %d times.', 'griffinforms-form-builder'), $edit_count);
        }

        $last_editor_id = absint($this->item->getProp('last_editor'));
        $last_edited    = (string) $this->item->getProp('last_edited');
        if ($edit_count > 0 && $last_editor_id > 0 && $last_edited !== '' && $last_edited !== '0000-00-00 00:00:00') {
            $last_editor = get_user_by('id', $last_editor_id);
            $editor_name = $last_editor && $last_editor->exists() ? $last_editor->display_name : '';
            $edited_date = $this->formatDateTime($last_edited);
            if ($editor_name !== '' && $edited_date !== '') {
                /* translators: 1: user name, 2: date */
                $lines[] = sprintf(__('Last edited by %1$s on %2$s.', 'griffinforms-form-builder'), $editor_name, $edited_date);
            }
        }

        if ($this->is_valid_form && $this->form) {
            $policy   = $this->form->getProp('submission_edit');
            $policy   = is_array($policy) ? $policy : [];
            $enabled  = !empty($policy['toggle']);
            $max_edits = isset($policy['max_edits']) ? absint($policy['max_edits']) : 0;

            if ($enabled && $max_edits > 0) {
                $remaining = max(0, $max_edits - $edit_count);
                if ($remaining === 0) {
                    $lines[] = __('No edits remaining.', 'griffinforms-form-builder');
                } elseif ($remaining === 1) {
                    $lines[] = __('1 edit remaining.', 'griffinforms-form-builder');
                } else {
                    /* translators: %d: edits remaining */
                    $lines[] = sprintf(__('%d edits remaining.', 'griffinforms-form-builder'), $remaining);
                }
            }

            if ($enabled) {
                $time     = isset($policy['time']) && is_array($policy['time']) ? $policy['time'] : [];
                $mode     = isset($time['mode']) ? sanitize_key((string) $time['mode']) : 'none';
                $now_ts   = current_time('timestamp', true);

                if ($mode === 'window') {
                    $end_ts = isset($time['end_ts']) ? absint($time['end_ts']) : 0;
                    if ($end_ts > 0) {
                        if ($now_ts > $end_ts) {
                            $lines[] = __('Editing window has closed.', 'griffinforms-form-builder');
                        } else {
                            $lines[] = sprintf(
                                /* translators: %s: date/time */
                                __('Editing open until %s.', 'griffinforms-form-builder'),
                                $this->formatDateTime($end_ts)
                            );
                        }
                    }
                } elseif ($mode === 'duration') {
                    $duration_seconds = (absint($time['duration_days'] ?? 0) * DAY_IN_SECONDS)
                        + (absint($time['duration_hours'] ?? 0) * HOUR_IN_SECONDS);
                    $created_raw = (string) $this->item->getProp('created');
                    $created_ts  = $created_raw !== '' ? strtotime($created_raw) : 0;
                    if ($duration_seconds > 0 && $created_ts > 0) {
                        $expires_ts = $created_ts + $duration_seconds;
                        if ($now_ts > $expires_ts) {
                            $lines[] = __('Editing window has closed.', 'griffinforms-form-builder');
                        } else {
                            $seconds_left = $expires_ts - $now_ts;
                            if ($seconds_left >= DAY_IN_SECONDS) {
                                $days = (int) ceil($seconds_left / DAY_IN_SECONDS);
                                $lines[] = $days === 1
                                    ? __('Less than 1 day left to edit.', 'griffinforms-form-builder')
                                    /* translators: %d: days */
                                    : sprintf(__('%d days left to edit.', 'griffinforms-form-builder'), $days);
                            } elseif ($seconds_left >= HOUR_IN_SECONDS) {
                                $hours = (int) ceil($seconds_left / HOUR_IN_SECONDS);
                                $lines[] = $hours === 1
                                    ? __('Less than 1 hour left to edit.', 'griffinforms-form-builder')
                                    /* translators: %d: hours */
                                    : sprintf(__('%d hours left to edit.', 'griffinforms-form-builder'), $hours);
                            } else {
                                $lines[] = __('Less than an hour left to edit.', 'griffinforms-form-builder');
                            }
                        }
                    }
                }
            }
        }

        $revision_count = count($this->getCachedRevisions((int) $this->item_id));

        echo '<div class="gf-submission-card gf-submission-edit-summary bg-white border rounded p-4 mt-4">';
        echo '<div class="text-uppercase text-muted small fw-semibold mb-3">' . esc_html__('Edit history', 'griffinforms-form-builder') . '</div>';
        echo '<ul class="gf-meta-list list-unstyled mb-0">';
        foreach ($lines as $line) {
            echo '<li class="small py-1 border-bottom">' . esc_html($line) . '</li>';
        }
        echo '</ul>';

        if ($revision_count >= 2) {
            echo '<div class="mt-3">';
            echo '<button type="button" class="button button-secondary" data-gf-compare-revisions-btn="1" data-gf-submission-id="' . esc_attr((string) $this->item_id) . '">';
            echo esc_html__('Compare revisions', 'griffinforms-form-builder');
            echo '</button>';
            echo '</div>';
        }

        echo '</div>';
    }

    private function getCachedRevisions(int $submission_id): array
    {
        if ($this->revision_list_cache !== null) {
            return $this->revision_list_cache;
        }
        $history = SubmissionHistoryManager::getInstance();
        $this->revision_list_cache = $history->getRevisions($submission_id, 50);
        return $this->revision_list_cache;
    }

    private function getRevisionListForJs(int $submission_id): array
    {
        $revisions = $this->getCachedRevisions($submission_id);
        $list      = [];
        foreach ($revisions as $r) {
            $list[] = array(
                'revision_number' => (int) ($r['revision_number'] ?? 0),
                'event_uid'       => (string) ($r['event_uid'] ?? ''),
                'event_type'      => (string) ($r['event_type'] ?? 'edit'),
                'created'         => (string) ($r['created'] ?? ''),
                'actor_label'     => (string) ($r['actor_label'] ?? ''),
            );
        }
        return $list;
    }

    private function renderMainColumn(): void
    {
        $this->renderFieldsSection();
    }

    private function renderRightSidebar(): void
    {
        $accordion_id = 'gf-submission-accordion-' . absint($this->item_id);
        echo '<div class="gf-submission-card bg-white rounded overflow-hidden">';
        echo '<div class="accordion gf-submission-accordion" id="' . esc_attr($accordion_id) . '">';
        $this->renderComplianceMetaAccordion($accordion_id);
        $this->renderHistoryAccordion($accordion_id);
        $this->renderAccountsAccordion($accordion_id);
        $this->renderLogsAccordion($accordion_id);
        echo '</div>';
        echo '</div>';
    }

    private function renderHistoryAccordion(string $accordion_id): void
    {
        $panel_id = 'gf-submission-history-' . absint($this->item_id);
        $history = SubmissionHistoryManager::getInstance();
        $revisions = $history->getRevisions((int) $this->item_id, 25);
        $current_revision_id = (int) $this->item->getProp('current_revision_id');

        echo '<div class="accordion-item gf-submission-accordion-item">';
        echo '<h2 class="accordion-header" id="' . esc_attr($panel_id) . '-header">';
        echo '<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#' . esc_attr($panel_id) . '" aria-expanded="false" aria-controls="' . esc_attr($panel_id) . '">';
        echo esc_html__('History & rollback', 'griffinforms-form-builder');
        echo '</button>';
        echo '</h2>';
        echo '<div id="' . esc_attr($panel_id) . '" class="accordion-collapse collapse" aria-labelledby="' . esc_attr($panel_id) . '-header" data-bs-parent="#' . esc_attr($accordion_id) . '">';
        echo '<div class="accordion-body">';
        if (!$this->canAccessSubmissionEditing() && $this->upsell_registry->shouldRenderLockedShell('submission_editing')) {
            echo '<div class="gf-submission-history-upsell">';
            $this->upsell_registry->render('submission_editing', 'card', [
                'badge' => __('Pro Preview', 'griffinforms-form-builder'),
            ]);
            echo '</div>';
        } else {
            echo '<div class="gf-submission-history-panel" data-gf-submission-history="1" data-gf-submission-id="' . esc_attr($this->item_id) . '" data-gf-current-revision-id="' . esc_attr((string) $current_revision_id) . '" data-gf-history-ajax="1">';
            echo '<div class="gf-submission-history-scroll pe-2 mb-3" style="max-height: 22rem; overflow-y: auto;">';

            if (empty($revisions)) {
                echo '<div class="text-muted small p-2">' . esc_html__('No submission revisions have been recorded yet.', 'griffinforms-form-builder') . '</div>';
            } else {
                foreach ($revisions as $revision) {
                    $revision_id = isset($revision['id']) ? (int) $revision['id'] : 0;
                    $event_uid = isset($revision['event_uid']) ? (string) $revision['event_uid'] : '';
                    $revision_number = isset($revision['revision_number']) ? (int) $revision['revision_number'] : 0;
                    $event_type = isset($revision['event_type']) ? (string) $revision['event_type'] : 'edit';
                    $actor_label = isset($revision['actor_label']) && $revision['actor_label'] !== '' ? (string) $revision['actor_label'] : __('Unknown user', 'griffinforms-form-builder');
                    $created = isset($revision['created']) ? $this->formatDateTime($revision['created']) : '';
                    $summary = isset($revision['summary']) ? (string) $revision['summary'] : '';
                    $delta = isset($revision['delta']) && is_array($revision['delta']) ? $revision['delta'] : array();
                    $snapshot = isset($revision['snapshot']) && is_array($revision['snapshot']) ? $revision['snapshot'] : array();
                    $snapshot_label = !empty($snapshot) ? wp_json_encode($snapshot, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '';
                    $delta_label = !empty($delta) ? wp_json_encode($delta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) : '';
                    $badge = $event_type === 'restore' ? __('Restore', 'griffinforms-form-builder') : ($event_type === 'create' ? __('Create', 'griffinforms-form-builder') : __('Edit', 'griffinforms-form-builder'));
                    $is_current = $revision_id > 0 && $revision_id === $current_revision_id;

                    $item_classes = array('gf-submission-history-item');
                    if ($is_current) {
                        $item_classes[] = 'is-current';
                    }
                    echo '<button type="button" class="' . esc_attr(implode(' ', $item_classes)) . '"';
                    echo ' data-gf-submission-history-item="1"';
                    echo ' data-gf-event-uid="' . esc_attr($event_uid) . '"';
                    echo ' data-gf-revision-id="' . esc_attr((string) $revision_id) . '"';
                    echo ' data-gf-revision-number="' . esc_attr((string) $revision_number) . '"';
                    echo ' data-gf-revision-label="' . esc_attr($summary) . '"';
                    echo ' data-gf-revision-created="' . esc_attr($created) . '"';
                    echo ' data-gf-revision-snapshot="' . esc_attr($snapshot_label) . '"';
                    echo ' data-gf-revision-delta="' . esc_attr($delta_label) . '"';
                    echo ' data-gf-revision-event-type="' . esc_attr($event_type) . '"';
                    echo ' data-gf-current-revision="' . esc_attr($is_current ? '1' : '0') . '">';
                    echo '<div class="gf-history-item-header">';
                    echo '<span class="gf-history-item-title">' . esc_html(sprintf(__('Revision #%d', 'griffinforms-form-builder'), $revision_number)) . '</span>';
                    echo '<span class="gf-history-item-badge gf-history-badge-' . esc_attr($event_type) . '">' . esc_html($badge) . '</span>';
                    echo '</div>';
                    echo '<div class="gf-history-item-meta">' . esc_html($summary) . '</div>';
                    echo '<div class="gf-history-item-meta">' . esc_html($actor_label) . ($created !== '' ? ' · ' . esc_html($created) : '') . '</div>';
                    if ($is_current) {
                        echo '<div class="gf-history-item-current">' . esc_html__('Current snapshot', 'griffinforms-form-builder') . '</div>';
                    }
                    echo '</button>';
                }
            }

            echo '</div>';
            echo '<div class="d-flex gap-2 mt-1">';
            echo '<button type="button" class="button" disabled data-gf-submission-history-preview-btn="1">' . esc_html__('Preview snapshot', 'griffinforms-form-builder') . '</button>';
            echo '<button type="button" class="button button-primary" disabled data-gf-submission-history-restore="1">' . esc_html__('Restore selected snapshot', 'griffinforms-form-builder') . '</button>';
            echo '</div>';
            echo '</div>'; // gf-submission-history-panel
        }
        echo '</div>'; // accordion-body
        echo '</div>'; // accordion-collapse
        echo '</div>'; // accordion-item
    }

    private function renderAccountsAccordion(string $accordion_id): void
    {
        $panel_id = 'gf-submission-accounts-' . absint($this->item_id);

        echo '<div class="accordion-item gf-submission-accordion-item">';
        echo '<h2 class="accordion-header" id="' . esc_attr($panel_id) . '-header">';
        echo '<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#' . esc_attr($panel_id) . '" aria-expanded="false" aria-controls="' . esc_attr($panel_id) . '">';
        echo esc_html__('User Registration', 'griffinforms-form-builder');
        echo '</button>';
        echo '</h2>';
        echo '<div id="' . esc_attr($panel_id) . '" class="accordion-collapse collapse" aria-labelledby="' . esc_attr($panel_id) . '-header" data-bs-parent="#' . esc_attr($accordion_id) . '">';
        echo '<div class="accordion-body gf-submission-accounts-body">';
        echo '<div id="gf-submission-accounts-panel" class="gf-submission-accounts-panel" data-gf-submission-id="' . esc_attr($this->item_id) . '">';
        echo '<div class="gf-submission-accounts-loading">';
        echo '<div class="placeholder-glow mb-2"><span class="placeholder col-9"></span></div>';
        echo '<div class="placeholder-glow mb-2"><span class="placeholder col-7"></span></div>';
        echo '<div class="placeholder-glow"><span class="placeholder col-10"></span></div>';
        echo '</div>';
        echo '</div>';
        echo '</div>';
        echo '</div>';
        echo '</div>';
    }

    private function renderFieldsSection(): void
    {
        echo '<div class="gf-submission-card bg-white border rounded p-4">';
        echo '<div class="d-flex justify-content-between align-items-center mb-3">';
        echo '<h5 class="mb-0">' . esc_html__('Submission details', 'griffinforms-form-builder') . '</h5>';
        echo '</div>';

        if (empty($this->submission_fields)) {
            echo '<div class="text-muted small">' . esc_html__('No submission data captured for this entry.', 'griffinforms-form-builder') . '</div>';
            echo '</div>';
            return;
        }

        foreach ($this->submission_fields as $entry) {
            $this->renderFieldEntry($entry);
        }

        echo '</div>';
    }

    private function renderFieldEntry(array $entry): void
    {
        $field_id = isset($entry['id']) ? (int) $entry['id'] : 0;
        $field_value = $entry['values'] ?? [];

        if ($field_id <= 0) {
            return;
        }

        $field_item = new \GriffinForms\Items\Field();
        $field_item->loadItem($field_id);
        $field_heading = (string) ($field_item->getProp('heading') ?? '');
        $field_name    = (string) ($field_item->getProp('name') ?? '');

        echo '<div class="gf-field-block mb-3"'
            . ' data-field-id="' . esc_attr((string) $field_id) . '"'
            . ' data-field-heading="' . esc_attr($field_heading) . '"'
            . ' data-field-name="' . esc_attr($field_name) . '"'
            . '>';

        if (!$this->fieldExists($field_id)) {
            $this->renderMissingFieldEntry($field_id, $field_value);
            echo '</div>';
            return;
        }

        $context = array(
            'submission_id' => $this->item_id,
            'decrypt_nonce' => $this->decrypt_nonce,
            'payment_status' => $this->payment_status,
            'payment_meta' => $this->payment_meta,
            'cart' => $this->cart,
            'form_id' => $this->item->getProp('form_id'),
            'accounts_email_field_ids' => $this->getAccountsEmailFieldIds(),
        );

        $field = new \GriffinForms\Admin\Html\Elements\Submission\Field($field_id, $field_value, $context);
        $field->getHtml();
        echo '</div>';
    }

    private function renderMissingFieldEntry(int $field_id, $field_value): void
    {
        echo '<div class="gf-field-missing bg-light p-3 rounded">';
        echo '<div class="d-flex align-items-center gap-2 mb-2">';
        echo '<span class="badge bg-secondary">' . esc_html__('Field removed', 'griffinforms-form-builder') . '</span>';
        echo '<span class="text-muted small">' . esc_html__('This field no longer exists in the form. Showing saved response.', 'griffinforms-form-builder') . '</span>';
        echo '</div>';
        $this->renderMissingFieldValue($field_value);
        echo '</div>';
    }

    private function renderMissingFieldValue($field_value): void
    {
        if (is_array($field_value)) {
            if ($this->isAssoc($field_value)) {
                echo '<pre class="bg-white border rounded small p-2 mb-0">' . esc_html(wp_json_encode($field_value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) . '</pre>';
            } else {
                foreach ($field_value as $value) {
                    $display = is_scalar($value) ? (string) $value : wp_json_encode($value, JSON_UNESCAPED_SLASHES);
                    echo '<div class="bg-white border rounded small p-2 mb-1">' . esc_html($display) . '</div>';
                }
            }
            return;
        }

        echo '<div class="bg-white border rounded small p-2 mb-0">' . esc_html((string) $field_value) . '</div>';
    }

    private function isAssoc(array $array): bool
    {
        return array_keys($array) !== range(0, count($array) - 1);
    }

    /**
     * Render submission fields from an arbitrary snapshot array using the same
     * pipeline as the live submission view. Called by the AJAX preview handler.
     *
     * @param array $snapshot  Decoded snapshot_json (page_N => [field_N => value, ...])
     * @param int   $submission_id  Used for context (decrypt nonce, etc.)
     */
    public function renderSnapshotFields(array $snapshot, int $submission_id): void
    {
        $fields = [];
        foreach ($snapshot as $page_id => $page_field_values) {
            if (!is_array($page_field_values)) {
                continue;
            }
            foreach ($page_field_values as $field_id => $field_values) {
                $field_id = absint(str_replace('field_', '', (string) $field_id));
                if ($field_id <= 0) {
                    continue;
                }
                $fields[] = array(
                    'id'     => $field_id,
                    'values' => $this->normalizeFieldValue($field_values),
                    'page'   => $this->normalizePageKey((string) $page_id),
                );
            }
        }

        if (empty($fields)) {
            echo '<div class="text-muted small">' . esc_html__('No submission data in this snapshot.', 'griffinforms-form-builder') . '</div>';
            return;
        }

        // Temporarily swap submission_fields so renderFieldEntry uses snapshot values
        $saved_fields      = $this->submission_fields;
        $saved_values      = $this->submission_field_values;
        $saved_item_id     = $this->item_id;

        $this->submission_fields = $fields;
        $this->submission_field_values = [];
        foreach ($fields as $entry) {
            $this->submission_field_values['field_' . $entry['id']] = $entry['values'];
            $this->submission_field_values[$entry['id']]            = $entry['values'];
        }
        $this->item_id = $submission_id;

        foreach ($this->submission_fields as $entry) {
            $this->renderFieldEntry($entry);
        }

        $this->submission_fields      = $saved_fields;
        $this->submission_field_values = $saved_values;
        $this->item_id                 = $saved_item_id;
    }

    private function fieldExists(int $field_id): bool
    {
        if (array_key_exists($field_id, $this->field_cache)) {
            return (bool) $this->field_cache[$field_id];
        }

        $exists = $this->sql->itemExists('field', $field_id);
        $this->field_cache[$field_id] = $exists;

        return $exists;
    }

    private function renderLogsAccordion(string $accordion_id): void
    {
        $panel_id = 'gf-submission-logs-' . absint($this->item_id);
        $timeline = new \GriffinForms\Admin\Html\Elements\LogsTimeline(
            'submission',
            $this->item_id,
            array(
                'empty_message' => __('No logs recorded for this submission yet.', 'griffinforms-form-builder'),
                'ajax' => true,
                'per_page' => 20,
                'include_jobs' => true,
            )
        );

        echo '<div class="accordion-item gf-submission-accordion-item">';
        echo '<h2 class="accordion-header" id="' . esc_attr($panel_id) . '-header">';
        echo '<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#' . esc_attr($panel_id) . '" aria-expanded="false" aria-controls="' . esc_attr($panel_id) . '">';
        echo esc_html__('Logs', 'griffinforms-form-builder');
        echo '</button>';
        echo '</h2>';
        echo '<div id="' . esc_attr($panel_id) . '" class="accordion-collapse collapse" aria-labelledby="' . esc_attr($panel_id) . '-header" data-bs-parent="#' . esc_attr($accordion_id) . '">';
        echo '<div class="accordion-body">';
        $timeline->html();
        echo '</div>';
        echo '</div>';
        echo '</div>';
    }

    private function renderComplianceMetaAccordion(string $accordion_id): void
    {
        $meta = $this->getSubmissionMetaPayload();
        if (empty($meta['has_data'])) {
            return;
        }

        $panel_id = 'gf-submission-compliance-meta-' . absint($this->item_id);
        echo '<div class="accordion-item gf-submission-accordion-item">';
        echo '<h2 class="accordion-header" id="' . esc_attr($panel_id) . '-header">';
        echo '<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#' . esc_attr($panel_id) . '" aria-expanded="false" aria-controls="' . esc_attr($panel_id) . '">';
        echo esc_html__('Compliance & metadata', 'griffinforms-form-builder');
        echo '</button>';
        echo '</h2>';
        echo '<div id="' . esc_attr($panel_id) . '" class="accordion-collapse collapse" aria-labelledby="' . esc_attr($panel_id) . '-header" data-bs-parent="#' . esc_attr($accordion_id) . '">';
        echo '<div class="accordion-body gf-submission-compliance-meta">';

        $notices = [];
        if (!empty($meta['compliance_context_missing'])) {
            $notices[] = __('Compliance context was not recorded for this submission. Showing available metadata only.', 'griffinforms-form-builder');
        }
        if (isset($meta['compliance_mode']) && $meta['compliance_mode'] === 0) {
            $notices[] = __('Compliance is off for this submission. Only metadata is shown.', 'griffinforms-form-builder');
        }
        if (isset($meta['form_exists']) && $meta['form_exists'] === false) {
            $notices[] = __('The original form was deleted. Some compliance data may be unavailable.', 'griffinforms-form-builder');
        }
        if (!empty($meta['compliance_mode']) && empty($meta['layout_snapshot_id'])) {
            $notices[] = __('No layout snapshot was recorded for this submission.', 'griffinforms-form-builder');
        }
        if (!empty($meta['compliance_mode'])
            && !empty($meta['layout_snapshot_id'])
            && !empty($meta['layout_snapshot_table_exists'])
            && isset($meta['layout_snapshot_exists'])
            && $meta['layout_snapshot_exists'] === false
            && !empty($meta['layout_version'])) {
            $notices[] = __('The layout snapshot referenced by this submission is missing.', 'griffinforms-form-builder');
        }
        if (!empty($meta['compliance_mode']) && empty($meta['events']) && empty($meta['ip']) && empty($meta['user_agent'])) {
            $notices[] = __('No compliance metadata was recorded for this submission.', 'griffinforms-form-builder');
        }
        if (!empty($notices)) {
            echo '<div class="d-flex flex-column gap-1 mb-3">';
            foreach ($notices as $notice) {
                echo '<div class="text-muted small"><em>' . esc_html($notice) . '</em></div>';
            }
            echo '</div>';
        }

        if (isset($meta['compliance_mode']) || !empty($meta['compliance_profile'])) {
            echo '<div class="mb-3 gf-compliance-meta-section">';
            echo '<div class="gf-compliance-meta-title">' . esc_html__('Compliance', 'griffinforms-form-builder') . '</div>';
            echo '<div class="d-flex flex-column gap-2">';
            $mode_label = '—';
            if ($meta['compliance_mode'] === 1) {
                $mode_label = __('On', 'griffinforms-form-builder');
            } elseif ($meta['compliance_mode'] === 0) {
                $mode_label = __('Off', 'griffinforms-form-builder');
            }
            echo '<div class="d-flex justify-content-between align-items-center">';
            echo '<span class="text-muted small">' . esc_html__('Mode', 'griffinforms-form-builder') . '</span>';
            echo '<span class="small">' . esc_html($mode_label) . '</span>';
            echo '</div>';
            if (!empty($meta['compliance_mode'])) {
                echo '<div class="d-flex justify-content-between align-items-center">';
                echo '<span class="text-muted small">' . esc_html__('Profile at submission', 'griffinforms-form-builder') . '</span>';
                $profile_display = $this->getComplianceProfileDisplay($meta['compliance_profile']);
                $profile_label = strtoupper($profile_display['label']);
                echo '<span class="small d-flex align-items-center gap-2">';
                if ($profile_display['icon_url'] !== '') {
                    echo '<img class="gf-compliance-icon" src="' . esc_url($profile_display['icon_url']) . '" alt="" aria-hidden="true">';
                }
                echo '<span>' . esc_html($profile_label) . '</span>';
                echo '</span>';
                echo '</div>';
                if (!empty($meta['compliance_flags']) && is_array($meta['compliance_flags'])) {
                    $flags = $meta['compliance_flags'];
                    echo '<div class="d-flex justify-content-between align-items-center">';
                    echo '<span class="text-muted small">' . esc_html__('Store IP', 'griffinforms-form-builder') . '</span>';
                    echo '<span class="small">' . esc_html(!empty($flags['store_ip']) ? __('Yes', 'griffinforms-form-builder') : __('No', 'griffinforms-form-builder')) . '</span>';
                    echo '</div>';
                    echo '<div class="d-flex justify-content-between align-items-center">';
                    echo '<span class="text-muted small">' . esc_html__('Store user agent', 'griffinforms-form-builder') . '</span>';
                    echo '<span class="small">' . esc_html(!empty($flags['store_user_agent']) ? __('Yes', 'griffinforms-form-builder') : __('No', 'griffinforms-form-builder')) . '</span>';
                    echo '</div>';
                    if (array_key_exists('retention_days', $flags)) {
                        $retention = absint($flags['retention_days']);
                        echo '<div class="d-flex justify-content-between align-items-center">';
                        echo '<span class="text-muted small">' . esc_html__('Retention days', 'griffinforms-form-builder') . '</span>';
                        echo '<span class="small">' . esc_html($retention > 0 ? (string) $retention : '—') . '</span>';
                        echo '</div>';
                    }
                }
            }
            echo '</div>';
            echo '</div>';
        }

        if (!empty($meta['form_id']) && !empty($meta['form_exists'])) {
            $issues = $this->getComplianceIssuesForForm((int) $meta['form_id']);
            if (!empty($issues)) {
                echo '<div class="mb-3 gf-compliance-meta-section">';
                echo '<div class="gf-compliance-meta-title">' . esc_html__('Compliance checks', 'griffinforms-form-builder') . '</div>';
                echo '<div class="d-flex flex-column gap-1">';
                foreach ($issues as $issue) {
                    if (!is_array($issue)) {
                        continue;
                    }
                    $message = isset($issue['message']) ? (string) $issue['message'] : '';
                    if ($message === '') {
                        continue;
                    }
                    echo '<div class="text-muted small">' . esc_html($message) . '</div>';
                }
                echo '</div>';
                echo '</div>';
            }
        }

        if (!empty($meta['ip']) || !empty($meta['user_agent'])) {
            echo '<div class="mb-3 gf-compliance-meta-section">';
            echo '<div class="gf-compliance-meta-title">' . esc_html__('Last‑seen', 'griffinforms-form-builder') . '</div>';
            echo '<div class="d-flex flex-column gap-2">';
            if (!empty($meta['ip'])) {
                echo '<div class="d-flex justify-content-between align-items-center">';
                echo '<span class="text-muted small">' . esc_html__('IP address', 'griffinforms-form-builder') . '</span>';
                echo '<span class="small gf-meta-chip-mono">' . esc_html($meta['ip']) . '</span>';
                echo '</div>';
            }
            if (!empty($meta['user_agent'])) {
                echo '<div class="text-muted small">' . esc_html__('User agent', 'griffinforms-form-builder') . '</div>';
                $ua_summary_data = $this->parseUserAgentSummary($meta['user_agent']);
                if (!empty($ua_summary_data['summary'])) {
                    $browser_icon = $this->getBrowserIconUrl($ua_summary_data['browser_key']);
                    echo '<div class="d-flex align-items-center gap-2">';
                    echo '<img class="gf-ua-icon" src="' . esc_url($browser_icon) . '" alt="" aria-hidden="true">';
                    echo '<div class="small fw-semibold">' . esc_html($ua_summary_data['summary']) . '</div>';
                    echo '</div>';
                }
                echo '<div class="small text-break text-muted">' . esc_html($meta['user_agent']) . '</div>';
            }
            echo '</div>';
            echo '</div>';
        }

        if (!empty($meta['source_url'])) {
            $display_url = strlen($meta['source_url']) > 80 ? substr($meta['source_url'], 0, 80) . '…' : $meta['source_url'];
            echo '<div class="mb-3 gf-compliance-meta-section">';
            echo '<div class="gf-compliance-meta-title">' . esc_html__('Submitted from', 'griffinforms-form-builder') . '</div>';
            echo '<div class="d-flex flex-column gap-2">';
            echo '<a href="' . esc_url($meta['source_url']) . '" target="_blank" rel="noopener noreferrer" class="small text-break gf-wp-link">' . esc_html($display_url) . '</a>';
            echo '</div>';
            echo '</div>';
        }

        if (!empty($meta['compliance_mode']) && (isset($meta['layout_version']) || isset($meta['current_layout_version']))) {
            echo '<div class="mb-3 gf-compliance-meta-section">';
            echo '<div class="gf-compliance-meta-title">' . esc_html__('Form version', 'griffinforms-form-builder') . '</div>';
            echo '<div class="d-flex flex-column gap-2">';
            echo '<div class="d-flex justify-content-between align-items-center">';
            echo '<span class="text-muted small">' . esc_html__('Submitted', 'griffinforms-form-builder') . '</span>';
            echo '<span class="small">' . esc_html($meta['layout_version'] !== null ? (string) $meta['layout_version'] : '—') . '</span>';
            echo '</div>';
            echo '<div class="d-flex justify-content-between align-items-center">';
            echo '<span class="text-muted small">' . esc_html__('Current', 'griffinforms-form-builder') . '</span>';
            echo '<span class="small">' . esc_html($meta['current_layout_version'] !== null ? (string) $meta['current_layout_version'] : '—') . '</span>';
            echo '</div>';
            if (!empty($meta['layout_version_mismatch'])) {
                echo '<div class="text-muted small"><em>' . esc_html__('Form layout has changed since this submission was captured.', 'griffinforms-form-builder') . '</em></div>';
            }
            echo '</div>';
            echo '</div>';
        }

        if (!empty($meta['events'])) {
            echo '<div class="gf-compliance-meta-section gf-compliance-meta-section-last">';
            echo '<div class="gf-compliance-meta-title">' . esc_html__('Event timeline', 'griffinforms-form-builder') . '</div>';
            echo '<div class="gf-log-timeline">';
            foreach ($meta['events'] as $event) {
                if (!is_array($event)) {
                    continue;
                }
                $page_id = isset($event['page_id']) ? (int) $event['page_id'] : 0;
                $event_type = isset($event['type']) ? (string) $event['type'] : '';
                $timestamp = isset($event['timestamp']) ? (string) $event['timestamp'] : '';
                $form_opened_at = isset($event['form_opened_at']) ? (string) $event['form_opened_at'] : '';
                $page_opened_at = isset($event['page_opened_at']) ? (string) $event['page_opened_at'] : '';
                $time_label = $this->formatMetaDateTime($timestamp);
                $fill_time = $this->formatMetaDuration($page_opened_at, $timestamp);
                $event_label = __('Page submitted', 'griffinforms-form-builder');
                $category_label = '';

                if ($event_type === 'payment_review_open') {
                    $event_label = __('Payment review opened', 'griffinforms-form-builder');
                    $category_label = __('Payment', 'griffinforms-form-builder');
                } elseif ($event_type === 'payment_submit') {
                    $event_label = __('Payment initiated', 'griffinforms-form-builder');
                    $category_label = __('Payment', 'griffinforms-form-builder');
                } elseif ($event_type === 'payment_confirmed') {
                    $event_label = __('Payment confirmed', 'griffinforms-form-builder');
                    $category_label = __('Payment', 'griffinforms-form-builder');
                } elseif ($event_type !== '' && $event_type !== 'page_submit') {
                    $event_label = __('Event recorded', 'griffinforms-form-builder');
                }

                $icon_data = $this->getComplianceEventIconData($event_type);
                echo '<div class="gf-log-entry">';
                echo '<span class="gf-log-node gf-log-node-info" data-gf-icon-expected="' . esc_attr($icon_data['expected']) . '"' . (!empty($icon_data['fallback']) ? ' data-gf-icon-fallback="1"' : '') . '>';
                if (!empty($icon_data['url'])) {
                    echo '<img src="' . esc_url($icon_data['url']) . '" alt="" aria-hidden="true">';
                }
                echo '</span>';
                echo '<div class="gf-log-content">';
                echo '<div class="gf-log-meta">';
                if ($category_label !== '') {
                    echo '<span class="gf-log-category">' . esc_html($category_label) . '</span>';
                } elseif ($page_id > 0) {
                    echo '<span class="gf-log-category">' . esc_html__('Page', 'griffinforms-form-builder') . ' ' . esc_html($page_id) . '</span>';
                }
                echo '</div>';
                if ($time_label !== '') {
                    echo '<div class="gf-log-time-row">';
                    echo '<span class="gf-log-time">' . esc_html($time_label) . '</span>';
                    echo '</div>';
                }
                echo '<div class="gf-log-message">';
                echo '<div class="gf-meta-event-title">' . esc_html($event_label) . '</div>';
                echo '<div class="gf-meta-event-details">';
                if ($fill_time !== '') {
                    echo '<span class="gf-meta-chip"><span class="gf-meta-chip-label">' . esc_html__('Fill‑time', 'griffinforms-form-builder') . '</span><span class="gf-meta-chip-value gf-meta-chip-mono">' . esc_html($fill_time) . '</span></span>';
                }
                $form_opened_label = $this->formatMetaDateTime($form_opened_at);
                if ($form_opened_label !== '') {
                    echo '<span class="gf-meta-chip"><span class="gf-meta-chip-label">' . esc_html__('Form opened', 'griffinforms-form-builder') . '</span><span class="gf-meta-chip-value">' . esc_html($form_opened_label) . '</span></span>';
                }
                $page_opened_label = $this->formatMetaDateTime($page_opened_at);
                if ($page_opened_label !== '') {
                    echo '<span class="gf-meta-chip"><span class="gf-meta-chip-label">' . esc_html__('Page opened', 'griffinforms-form-builder') . '</span><span class="gf-meta-chip-value">' . esc_html($page_opened_label) . '</span></span>';
                }
                echo '</div>';
                echo '</div>';
                echo '</div>';
                echo '</div>';
            }
            echo '</div>';
            echo '</div>';
        } else {
            echo '<div class="gf-compliance-meta-section gf-compliance-meta-section-last">';
            echo '<div class="gf-compliance-meta-title">' . esc_html__('Event timeline', 'griffinforms-form-builder') . '</div>';
            echo '<div class="text-muted small">' . esc_html__('No metadata events recorded for this submission.', 'griffinforms-form-builder') . '</div>';
            echo '</div>';
        }

        echo '</div>';
        echo '</div>';
        echo '</div>';
    }

    private function getSubmissionMetaPayload(): array
    {
        if (is_array($this->submission_meta_payload)) {
            return $this->submission_meta_payload;
        }

        $payload = array(
            'ip' => '',
            'user_agent' => '',
            'events' => array(),
            'has_data' => false,
            'layout_version' => null,
            'current_layout_version' => null,
            'layout_version_mismatch' => false,
            'form_id' => 0,
            'form_exists' => true,
            'layout_snapshot_id' => 0,
            'layout_snapshot_exists' => null,
            'layout_snapshot_table_exists' => null,
            'compliance_mode' => null,
            'compliance_profile' => '',
            'compliance_flags' => array(),
            'compliance_context_missing' => false,
            'accounts' => array(),
            'marketing' => array(),
            'source_url' => '',
        );

        if ($this->item_id <= 0) {
            $this->submission_meta_payload = $payload;
            return $this->submission_meta_payload;
        }

        global $wpdb;
        $submission_table = $this->config->getTable('submission');
        $form_table = $this->config->getTable('form');
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT submission_ip, submission_user_agent, submission_meta, source_url, form_id, form_layout_version, layout_snapshot_id FROM %i WHERE id = %d",
                $submission_table,
                $this->item_id
            ),
            ARRAY_A
        );

        if (!is_array($row)) {
            $this->submission_meta_payload = $payload;
            return $this->submission_meta_payload;
        }

        $payload['ip'] = isset($row['submission_ip']) ? (string) $row['submission_ip'] : '';
        $payload['user_agent'] = isset($row['submission_user_agent']) ? (string) $row['submission_user_agent'] : '';
        $payload['source_url'] = isset($row['source_url']) ? (string) $row['source_url'] : '';
        $payload['layout_version'] = isset($row['form_layout_version']) ? (int) $row['form_layout_version'] : null;
        $payload['layout_snapshot_id'] = isset($row['layout_snapshot_id']) ? (int) $row['layout_snapshot_id'] : 0;
        $form_id = isset($row['form_id']) ? (int) $row['form_id'] : 0;
        $payload['form_id'] = $form_id;
        if ($form_id > 0) {
            $form_exists = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT id FROM %i WHERE id = %d",
                    $form_table,
                    $form_id
                )
            );
            if ($form_exists === null) {
                $payload['form_exists'] = false;
            } else {
                $current_version = $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT layout_version FROM %i WHERE id = %d",
                        $form_table,
                        $form_id
                    )
                );
                if ($current_version !== null && $current_version !== '') {
                    $payload['current_layout_version'] = (int) $current_version;
                }
            }
        }
        if ($payload['layout_version'] !== null && $payload['current_layout_version'] !== null && $payload['layout_version'] > 0) {
            $payload['layout_version_mismatch'] = $payload['layout_version'] !== $payload['current_layout_version'];
        }
        $raw_meta = isset($row['submission_meta']) ? $row['submission_meta'] : '';
        $decoded = $this->maybeDecode($raw_meta);
        if (is_array($decoded) && !empty($decoded['events']) && is_array($decoded['events'])) {
            $payload['events'] = $decoded['events'];
        }
        if (is_array($decoded) && isset($decoded['accounts']) && is_array($decoded['accounts'])) {
            $payload['accounts'] = $decoded['accounts'];
        }
        if (is_array($decoded)) {
            $mailchimp_key = \GriffinForms\Includes\Integrations\Mailchimp\Contract::SUBMISSION_META_KEY;
            if (isset($decoded[$mailchimp_key]) && is_array($decoded[$mailchimp_key])) {
                $payload['marketing']['mailchimp'] = $decoded[$mailchimp_key];
            }
            if (isset($decoded['klaviyo']) && is_array($decoded['klaviyo'])) {
                $payload['marketing']['klaviyo'] = $decoded['klaviyo'];
            }
        }
        if (is_array($decoded) && isset($decoded['compliance']) && is_array($decoded['compliance'])) {
            $payload['compliance_mode'] = array_key_exists('mode', $decoded['compliance']) ? (int) $decoded['compliance']['mode'] : null;
            $payload['compliance_profile'] = isset($decoded['compliance']['profile']) ? (string) $decoded['compliance']['profile'] : '';
            $payload['compliance_flags'] = isset($decoded['compliance']['flags']) && is_array($decoded['compliance']['flags'])
                ? $decoded['compliance']['flags']
                : array();
        } else {
            $payload['compliance_context_missing'] = true;
        }
        $snapshot_table = $this->config->getTable('layout_snapshot');
        $table_exists = $wpdb->get_var(
            $wpdb->prepare(
                "SHOW TABLES LIKE %s",
                $snapshot_table
            )
        );
        $payload['layout_snapshot_table_exists'] = $table_exists !== null;
        if (!empty($payload['layout_snapshot_table_exists'])) {
            if ($payload['layout_snapshot_id'] > 0) {
                $snapshot_exists = $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT id FROM %i WHERE id = %d",
                        $snapshot_table,
                        $payload['layout_snapshot_id']
                    )
                );
                $payload['layout_snapshot_exists'] = $snapshot_exists !== null;
            }
            if (empty($payload['layout_snapshot_id']) || $payload['layout_snapshot_exists'] === false) {
                $fallback_id = $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT id FROM %i WHERE submission_id = %d ORDER BY id DESC LIMIT 1",
                        $snapshot_table,
                        $this->item_id
                    )
                );
                if ($fallback_id !== null && $fallback_id !== '') {
                    $payload['layout_snapshot_id'] = (int) $fallback_id;
                    $payload['layout_snapshot_exists'] = true;
                }
            }
        }

        if ($payload['ip'] !== '' || $payload['user_agent'] !== '' || !empty($payload['events']) || $payload['layout_version'] !== null || $payload['current_layout_version'] !== null || $payload['compliance_mode'] !== null || $payload['layout_snapshot_id'] > 0 || $payload['compliance_context_missing'] || $payload['form_exists'] === false) {
            $payload['has_data'] = true;
        }

        $this->submission_meta_payload = $payload;

        return $this->submission_meta_payload;
    }

    private function getAccountsEmailFieldIds(): array
    {
        if (is_array($this->accounts_email_field_ids)) {
            return $this->accounts_email_field_ids;
        }

        $this->accounts_email_field_ids = array();
        $meta = $this->getSubmissionMetaPayload();
        $accounts = isset($meta['accounts']) && is_array($meta['accounts']) ? $meta['accounts'] : array();
        $setups = isset($accounts['setups']) && is_array($accounts['setups']) ? $accounts['setups'] : array();

        foreach ($setups as $setup) {
            $field_id = isset($setup['email_field_id']) ? absint($setup['email_field_id']) : 0;
            if ($field_id > 0) {
                $this->accounts_email_field_ids[$field_id] = $field_id;
            }
        }

        return $this->accounts_email_field_ids;
    }

    private function getComplianceIssuesForForm(int $form_id): array
    {
        if ($form_id <= 0) {
            return [];
        }

        $checks = new \GriffinForms\Includes\Checks\Form();
        $issues = $checks->run($form_id);
        if (!is_array($issues)) {
            return [];
        }

        return array_values(array_filter($issues, static function ($issue) {
            if (!is_array($issue)) {
                return false;
            }
            $code = isset($issue['code']) ? (string) $issue['code'] : '';
            return $code !== '' && strpos($code, 'compliance_') === 0;
        }));
    }

    private function getComplianceEventIconData(string $event_type): array
    {
        $map = array(
            'page_submit' => 'submission-frontend.svg',
            'payment_review_open' => 'payment-integration.svg',
            'payment_submit' => 'payment-integration.svg',
            'payment_confirmed' => 'payment-integration.svg',
        );

        $expected = isset($map[$event_type]) ? $map[$event_type] : 'fallback.svg';
        $base_path = $this->config->getRootPath() . 'admin/images/log-timeline-icons/';
        $base_url = $this->config->getRootUrl() . 'admin/images/log-timeline-icons/';

        $candidate_path = $base_path . $expected;
        if (file_exists($candidate_path)) {
            return array(
                'url' => $base_url . $expected,
                'expected' => $expected,
                'fallback' => $expected === 'fallback.svg',
            );
        }

        $fallback_path = $base_path . 'fallback.svg';
        if (file_exists($fallback_path)) {
            return array(
                'url' => $base_url . 'fallback.svg',
                'expected' => $expected,
                'fallback' => true,
            );
        }

        return array(
            'url' => '',
            'expected' => $expected,
            'fallback' => true,
        );
    }

    private function getComplianceProfileDisplay(string $profile): array
    {
        $profile = strtolower(trim($profile));
        $label = '—';
        $icon_key = '';

        if ($profile === 'hipaa') {
            $label = __('HIPAA‑ready', 'griffinforms-form-builder');
            $icon_key = 'hipaa-ready';
        } elseif ($profile === 'gdpr') {
            $label = __('GDPR', 'griffinforms-form-builder');
            $icon_key = 'gdpr';
        } elseif ($profile !== '') {
            $label = strtoupper($profile);
        }

        $icon_url = '';
        if ($icon_key !== '') {
            $base_path = $this->config->getRootPath() . 'admin/images/compliance-icons/';
            $base_url = $this->config->getRootUrl() . 'admin/images/compliance-icons/';
            $svg_path = $base_path . $icon_key . '.svg';
            $png_path = $base_path . $icon_key . '.png';
            if (file_exists($svg_path)) {
                $icon_url = $base_url . $icon_key . '.svg';
            } elseif (file_exists($png_path)) {
                $icon_url = $base_url . $icon_key . '.png';
            }
        }

        return array(
            'label' => $label,
            'icon_url' => $icon_url,
        );
    }

    private function formatMetaDateTime(string $value): string
    {
        $value = trim($value);
        if ($value === '') {
            return '';
        }

        if (ctype_digit($value)) {
            $timestamp = (int) $value;
        } else {
            $timestamp = strtotime($value);
        }

        if (!$timestamp) {
            return '';
        }

        $format = get_option('date_format') . ' ' . get_option('time_format');
        return wp_date($format, $timestamp);
    }

    private function formatMetaDuration(string $start_value, string $end_value): string
    {
        $start_ts = $this->parseMetaTimestamp($start_value);
        $end_ts = $this->parseMetaTimestamp($end_value);
        if ($start_ts <= 0 || $end_ts <= 0 || $end_ts < $start_ts) {
            return '';
        }
        $seconds = $end_ts - $start_ts;
        $hours = (int) floor($seconds / 3600);
        $minutes = (int) floor(($seconds % 3600) / 60);
        $secs = (int) ($seconds % 60);
        return sprintf('%02d:%02d:%02d', $hours, $minutes, $secs);
    }

    private function parseMetaTimestamp(string $value): int
    {
        $value = trim($value);
        if ($value === '') {
            return 0;
        }
        if (ctype_digit($value)) {
            return (int) $value;
        }
        $ts = strtotime($value);
        return $ts ? (int) $ts : 0;
    }

    private function parseUserAgentSummary(string $ua): array
    {
        $ua = trim($ua);
        if ($ua === '') {
            return array(
                'summary' => '',
                'browser_key' => 'fallback',
            );
        }

        // Handle already-summarized UA strings (e.g., "Safari 17.2 · macOS 14.2").
        if (strpos($ua, '·') !== false) {
            $parts = array_map('trim', explode('·', $ua));
            $first = $parts[0] ?? '';
            if ($first !== '') {
                if (stripos($first, 'Edge') === 0) {
                    return array('summary' => $ua, 'browser_key' => 'edge');
                }
                if (stripos($first, 'Chrome') === 0) {
                    return array('summary' => $ua, 'browser_key' => 'chrome');
                }
                if (stripos($first, 'Firefox') === 0) {
                    return array('summary' => $ua, 'browser_key' => 'firefox');
                }
                if (stripos($first, 'Safari') === 0) {
                    return array('summary' => $ua, 'browser_key' => 'safari');
                }
            }
        }

        $browser = '';
        $version = '';
        $browser_key = 'fallback';

        if (preg_match('/Edg\\/([\\d\\.]+)/i', $ua, $match)) {
            $browser = 'Edge';
            $version = $match[1];
            $browser_key = 'edge';
        } elseif (preg_match('/Chrome\\/([\\d\\.]+)/i', $ua, $match) && stripos($ua, 'Edg/') === false) {
            $browser = 'Chrome';
            $version = $match[1];
            $browser_key = 'chrome';
        } elseif (preg_match('/Firefox\\/([\\d\\.]+)/i', $ua, $match)) {
            $browser = 'Firefox';
            $version = $match[1];
            $browser_key = 'firefox';
        } elseif (preg_match('/Version\\/([\\d\\.]+).*Safari/i', $ua, $match)) {
            $browser = 'Safari';
            $version = $match[1];
            $browser_key = 'safari';
        }

        $os = '';
        if (preg_match('/Mac OS X ([\\d_\\.]+)/i', $ua, $match)) {
            $os = 'macOS ' . str_replace('_', '.', $match[1]);
        } elseif (preg_match('/Windows NT ([\\d\\.]+)/i', $ua, $match)) {
            $os = 'Windows ' . $match[1];
        } elseif (preg_match('/Android ([\\d\\.]+)/i', $ua, $match)) {
            $os = 'Android ' . $match[1];
        } elseif (preg_match('/iPhone OS ([\\d_]+)/i', $ua, $match)) {
            $os = 'iOS ' . str_replace('_', '.', $match[1]);
        } elseif (preg_match('/iPad; CPU OS ([\\d_]+)/i', $ua, $match)) {
            $os = 'iPadOS ' . str_replace('_', '.', $match[1]);
        }

        if ($browser === '' && $os === '') {
            return array(
                'summary' => '',
                'browser_key' => 'fallback',
            );
        }

        $parts = [];
        if ($browser !== '') {
            $parts[] = trim($browser . ' ' . $version);
        }
        if ($os !== '') {
            $parts[] = $os;
        }

        return array(
            'summary' => implode(' · ', $parts),
            'browser_key' => $browser_key !== '' ? $browser_key : 'fallback',
        );
    }

    private function getBrowserIconUrl(string $browser_key): string
    {
        $browser_key = $browser_key !== '' ? $browser_key : 'fallback';
        $base_path = $this->config->getRootPath() . 'admin/images/browser-icons/';
        $base_url = $this->config->getRootUrl() . 'admin/images/browser-icons/';
        $candidate_svg = $base_path . $browser_key . '.svg';
        if (file_exists($candidate_svg)) {
            return $base_url . $browser_key . '.svg';
        }
        $candidate_png = $base_path . $browser_key . '.png';
        if (file_exists($candidate_png)) {
            return $base_url . $browser_key . '.png';
        }
        $fallback_path = $base_path . 'fallback.svg';
        if (file_exists($fallback_path)) {
            return $base_url . 'fallback.svg';
        }
        return $this->config->getRootUrl() . 'admin/images/log-timeline-icons/fallback.svg';
    }

    private function getFormPagesHtml()
    {
        if (!is_array($this->formpages) || empty($this->formpages)) {
            return;
        }

        foreach ($this->formpages as $formpage_id) {
            $this->getFormPageHtml($formpage_id);
        }
    }

    private function getFormPageHtml($id)
    {
        echo '<div class="p-5 bg-white rounded my-3 w-75 border">';

        $this->getFormPageHeadingHtml($id);

        if ($this->sql->itemExists('formpage', $id)) {
            $this->structuredFieldsHtml($id);
        } else {
            // $this->unstructuredFieldsHtml($id);
        }

        echo '</div>';
    }

    private function getFormPageHeadingHtml($id)
    {
        if ($this->sql->itemExists('formpage', $id)) {
            
            $formpage = new \GriffinForms\Items\FormPage();
            $formpage->loadItem($id);
            
            echo '<div class="mb-1 text-secondary small">';
            echo esc_html($formpage->getProp('name'));
            echo '</div>';

            echo '<div class="mb-1"><h5>';
            echo esc_html($formpage->getProp('heading'));
            echo '</h5></div>';

            echo '<div class="mb-3 pb-3">';
            echo esc_html($formpage->getProp('description'));
            echo '</div>';
        } else {
            echo '<div class="gf-notice gf-notice-danger" role="alert">';
            esc_html_e('This page no longer exists', 'griffinforms-form-builder');
            echo '</div>';
        }
    }

    private function structuredFieldsHtml($page_id)
    {
        $formpage = new \GriffinForms\Items\FormPage();
        $formpage->loadItem($page_id);

        $pagerows = $formpage->getProp('pagerows');

        if (!is_array($pagerows) or empty($pagerows)) {
            return;
        }

        foreach ($pagerows as $pagerow_id) {
            $this->getPageRowHtml($pagerow_id);
        }
    }

    private function getPageRowHtml($pagerow_id)
    {
        if (!$this->sql->itemExists('pagerow', $pagerow_id)) {
            return;
        }

        $pagerow = new \GriffinForms\Items\PageRow();
        $pagerow->loadItem($pagerow_id);

        echo '<div class="">';

        echo '<div class="text-secondary small">';
        echo esc_html($pagerow->getProp('name'));
        echo '</div>';
        echo '<div class="fw-bold">';
        echo esc_html($pagerow->getProp('heading'));
        echo '</div>';

        echo '<div class="row mt-2 mb-4">';

        $rowcolumns = $pagerow->getProp('rowcolumns');

        foreach ($rowcolumns as $rowcolumn_id) {
            $this->getRowColumnHtml($rowcolumn_id);
        }
        echo '</div>';

        echo '</div>';

    }

    private function getRowColumnHtml($rowcolumn_id)
    {
        if (!$this->sql->itemExists('rowcolumn', $rowcolumn_id)) {
            return;
        }

        $rowcolumn = new \GriffinForms\Items\RowColumn();
        $rowcolumn->loadItem($rowcolumn_id);

        $col_class = 'col-md-' . $rowcolumn->getProp('width');
        echo '<div class="' . esc_attr($col_class) . '">';

        $fields = $rowcolumn->getProp('fields');

        if (is_array($fields)) {
            foreach ($fields as $field_id) {
                $this->getFieldHtml($field_id);
            }
        }

        echo '</div>';
    }

    private function getFieldHtml($field_id)
    {
        if (empty($field_id)) {
            return;
        }


        $field_key = 'field_' . $field_id;
        $field_value = isset($this->submission_field_values[$field_key]) ? $this->submission_field_values[$field_key] : [];
        $context = array(
            'submission_id' => $this->item_id,
            'decrypt_nonce' => $this->decrypt_nonce,
            'payment_status' => $this->payment_status,
            'payment_meta' => $this->payment_meta,
            'cart' => $this->cart,
            'form_id' => $this->item->getProp('form_id')
        );
        $field = new \GriffinForms\Admin\Html\Elements\Submission\Field($field_id, $field_value, $context);
        $field->getHtml();
    }

    private function renderPaymentSummaryBlock(): void
    {
        if (!$this->is_payment_form) {
            return;
        }
        $status_label = $this->paymentStatusBadge($this->payment_status);
        $resume_url = $this->getResumeUrl();
        $currency_helper = Currency::getInstance();
        $cart_items = is_array($this->cart['items'] ?? null) ? $this->cart['items'] : [];
        $cart_currency = $this->cart['currency'] ?? 'USD';
        $cart_subtotal = isset($this->cart['subtotal']) ? (float) $this->cart['subtotal'] : 0;
        $cart_tax_total = isset($this->cart['tax_total']) ? (float) $this->cart['tax_total'] : 0;
        $cart_total = isset($this->cart['total']) ? (float) $this->cart['total'] : ($cart_subtotal + $cart_tax_total);
        $gateway = $this->resolveGatewayName();
        $provider = $this->getPaymentProviderMeta($gateway);
        $provider_icon = isset($provider['icon']) ? esc_url($provider['icon']) : '';
        $provider_dashboard = isset($provider['dashboard']) ? esc_url($provider['dashboard']) : '';
        $intent_id = $this->payment_meta['stripe']['intent_id'] ?? '';
        $client_secret = $this->payment_meta['stripe']['client_secret'] ?? '';
        $order_id = $this->payment_meta['paypal']['order_id'] ?? '';
        $capture_id = $this->payment_meta['paypal']['capture_id'] ?? '';
        $sq_payment_id = $this->payment_meta['square']['payment_id'] ?? '';
        $gateway_label = isset($provider['name']) && is_string($provider['name']) && $provider['name'] !== ''
            ? $provider['name']
            : ucfirst($gateway);

        echo '<div class="gf-submission-card gf-payment-summary border rounded bg-white p-4 mb-4">';
        echo '<div class="d-flex flex-wrap justify-content-between align-items-start gap-3 mb-3">';
        echo '<div>';
        echo '<div class="text-uppercase text-muted small fw-semibold">' . esc_html__('Payment', 'griffinforms-form-builder') . '</div>';
        if (!empty($gateway)) {
            echo '<div class="small text-muted">' . sprintf(esc_html__('Gateway: %s', 'griffinforms-form-builder'), esc_html($gateway_label)) . '</div>';
        }
        echo '</div>';
        echo '<div class="d-flex flex-wrap gap-2 align-items-center">';
        if (!empty($provider_icon)) {
            echo '<span class="d-inline-flex align-items-center"><img src="' . $provider_icon . '" alt="' . esc_attr($gateway_label) . '" style="height:24px;width:auto;"></span>';
        }
        echo wp_kses_post($status_label);
        if (!empty($provider_dashboard) && (!empty($intent_id) || !empty($order_id))) {
            echo '<a class="btn btn-link btn-sm text-decoration-none" href="' . esc_url($provider_dashboard) . '" target="_blank" rel="noopener noreferrer">';
            echo esc_html__('Open in dashboard', 'griffinforms-form-builder');
            echo '</a>';
        }
        echo '</div>';
        echo '</div>';

        if (!empty($cart_items)) {
            echo '<div class="gf-payment-line-items mb-3">';
            foreach ($cart_items as $entry) {
                $label = $entry['label'] ?? ($entry['product_id'] ?? __('Product', 'griffinforms-form-builder'));
                $qty = (int) ($entry['quantity'] ?? 1);
                $line_total = isset($entry['subtotal']) ? (float) $entry['subtotal'] : 0;
                $line_currency = $entry['currency'] ?? $cart_currency;
                echo '<div class="d-flex justify-content-between align-items-start mb-2">';
                echo '<div class="text-muted small">';
                echo '<div class="fw-semibold text-dark">' . esc_html($label) . ($qty > 1 ? ' × ' . $qty : '') . '</div>';
                echo '</div>';
                echo '<div class="fw-semibold small">' . esc_html($currency_helper->formatAmount($line_total, $line_currency)) . '</div>';
                echo '</div>';
            }
            echo '</div>';
        } else {
            echo '<div class="text-muted small mb-3">' . esc_html__('No cart data stored for this submission.', 'griffinforms-form-builder') . '</div>';
        }

        echo '<div class="gf-payment-totals border-top pt-3">';
        echo '<div class="d-flex justify-content-between text-muted small"><span>' . esc_html__('Subtotal', 'griffinforms-form-builder') . '</span><span>' . esc_html($currency_helper->formatAmount($cart_subtotal, $cart_currency)) . '</span></div>';
        if ($cart_tax_total > 0) {
            echo '<div class="d-flex justify-content-between text-muted small"><span>' . esc_html__('Tax', 'griffinforms-form-builder') . '</span><span>' . esc_html($currency_helper->formatAmount($cart_tax_total, $cart_currency)) . '</span></div>';
        }
        echo '<div class="d-flex justify-content-between fw-semibold mt-2"><span>' . esc_html__('Total', 'griffinforms-form-builder') . '</span><span>' . esc_html($currency_helper->formatAmount($cart_total, $cart_currency)) . '</span></div>';
        echo '</div>';

        if (!empty($intent_id) || !empty($client_secret) || !empty($order_id) || !empty($capture_id) || !empty($sq_payment_id)) {
            echo '<div class="text-muted small mt-3">';
            if (!empty($intent_id)) {
                echo '<div>' . esc_html__('Intent ID:', 'griffinforms-form-builder') . ' ' . esc_html($intent_id) . '</div>';
            }
            if (!empty($client_secret)) {
                echo '<div>' . esc_html__('Client secret stored.', 'griffinforms-form-builder') . '</div>';
            }
            if (!empty($order_id)) {
                echo '<div>' . esc_html__('Order ID:', 'griffinforms-form-builder') . ' ' . esc_html($order_id) . '</div>';
            }
            if (!empty($capture_id)) {
                echo '<div>' . esc_html__('Capture ID:', 'griffinforms-form-builder') . ' ' . esc_html($capture_id) . '</div>';
            }
            if (!empty($sq_payment_id)) {
                echo '<div>' . esc_html__('Payment ID:', 'griffinforms-form-builder') . ' ' . esc_html($sq_payment_id) . '</div>';
            }
            echo '</div>';
        }

        echo '</div>';
    }

    private function paymentStatusBadge(string $status): string
    {
        $normalized = strtolower(trim($status));
        $map = array(
            'paid'      => array('label' => __('Paid', 'griffinforms-form-builder'), 'class' => 'badge bg-success'),
            'pending'   => array('label' => __('Pending Payment', 'griffinforms-form-builder'), 'class' => 'badge bg-warning text-dark'),
            'failed'    => array('label' => __('Failed', 'griffinforms-form-builder'), 'class' => 'badge bg-danger'),
            'abandoned' => array('label' => __('Abandoned', 'griffinforms-form-builder'), 'class' => 'badge bg-secondary'),
        );
        $fallback = array('label' => ucfirst($normalized ?: __('Unknown', 'griffinforms-form-builder')), 'class' => 'badge bg-secondary');
        $data = $map[$normalized] ?? $fallback;
        return '<span class="' . esc_attr($data['class']) . '">' . esc_html($data['label']) . '</span>';
    }

    private function getMarketingActivityCards(): array
    {
        if (is_array($this->marketing_activity_cards)) {
            return $this->marketing_activity_cards;
        }

        $cards = [];
        $mailchimp_card = $this->buildMailchimpMarketingCard();
        if (is_array($mailchimp_card) && !empty($mailchimp_card)) {
            $cards[] = $mailchimp_card;
        }

        $klaviyo_card = $this->buildKlaviyoMarketingCard();
        if (is_array($klaviyo_card) && !empty($klaviyo_card)) {
            $cards[] = $klaviyo_card;
        }

        $this->marketing_activity_cards = $cards;

        return $this->marketing_activity_cards;
    }

    private function buildMailchimpMarketingCard(): ?array
    {
        $meta = $this->getSubmissionMetaPayload();
        $marketing = isset($meta['marketing']) && is_array($meta['marketing']) ? $meta['marketing'] : [];
        $state = isset($marketing['mailchimp']) && is_array($marketing['mailchimp']) ? $marketing['mailchimp'] : [];
        if (empty($state)) {
            return null;
        }

        $providers = Providers::getInstance();
        $provider = $providers->get('mailchimp', true);
        $provider_label = is_array($provider) ? trim((string) ($provider['name'] ?? 'Mailchimp')) : 'Mailchimp';
        $provider_icon = is_array($provider)
            ? trim((string) ($provider['icon'] ?? ($provider['icon_dark'] ?? '')))
            : '';
        $status = sanitize_key((string) ($state['status'] ?? ''));
        if ($status === '') {
            $status = sanitize_key((string) ($state['member_status'] ?? ''));
        }

        $rows = [];
        $audience_id = sanitize_text_field((string) ($state['audience_id'] ?? ''));
        if ($audience_id !== '') {
            $rows[] = ['label' => __('Audience', 'griffinforms-form-builder'), 'value' => $audience_id];
        }

        $subscriber_email = sanitize_email((string) ($state['subscriber_email'] ?? ''));
        if ($subscriber_email !== '') {
            $rows[] = ['label' => __('Subscriber', 'griffinforms-form-builder'), 'value' => $subscriber_email];
        }

        $subscription_status = sanitize_key((string) ($state['subscription_status'] ?? ''));
        if ($subscription_status !== '') {
            $rows[] = ['label' => __('Requested mode', 'griffinforms-form-builder'), 'value' => $this->formatMarketingStateLabel($subscription_status)];
        }

        $member_status = sanitize_key((string) ($state['member_status'] ?? ''));
        if ($member_status !== '') {
            $rows[] = ['label' => __('Member status', 'griffinforms-form-builder'), 'value' => $this->formatMarketingStateLabel($member_status)];
        }

        $operation = sanitize_key((string) ($state['operation'] ?? ''));
        if ($operation !== '') {
            $rows[] = ['label' => __('Operation', 'griffinforms-form-builder'), 'value' => $this->formatMarketingStateLabel($operation)];
        }

        $tags = isset($state['tags']) && is_array($state['tags']) ? array_values(array_filter(array_map('sanitize_text_field', $state['tags']))) : [];
        if (!empty($tags)) {
            $rows[] = ['label' => __('Tags', 'griffinforms-form-builder'), 'value' => implode(', ', $tags)];
        }

        if (!empty($state['consent_required'])) {
            $rows[] = [
                'label' => __('Consent', 'griffinforms-form-builder'),
                'value' => !empty($state['consent_granted'])
                    ? __('Required and accepted', 'griffinforms-form-builder')
                    : __('Required but not accepted', 'griffinforms-form-builder'),
            ];
        }

        $reason = sanitize_key((string) ($state['reason'] ?? ''));
        if ($reason !== '') {
            $rows[] = ['label' => __('Skip reason', 'griffinforms-form-builder'), 'value' => $this->formatMarketingStateLabel($reason)];
        }

        $last_synced_at = $this->formatDateTime((string) ($state['last_synced_at'] ?? ''));
        if ($last_synced_at !== '') {
            $rows[] = ['label' => __('Last synced', 'griffinforms-form-builder'), 'value' => $last_synced_at];
        }

        $last_attempted_at = $this->formatDateTime((string) ($state['last_attempted_at'] ?? ''));
        if ($last_attempted_at !== '' && $last_synced_at === '') {
            $rows[] = ['label' => __('Last attempted', 'griffinforms-form-builder'), 'value' => $last_attempted_at];
        }

        $updated_at = $this->formatDateTime((string) ($state['updated_at'] ?? ''));
        if ($updated_at !== '') {
            $rows[] = ['label' => __('State updated', 'griffinforms-form-builder'), 'value' => $updated_at];
        }

        $error = sanitize_text_field((string) ($state['last_error'] ?? ''));
        if ($error !== '' && $status === \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_FAILED) {
            $rows[] = ['label' => __('Last error', 'griffinforms-form-builder'), 'value' => $error];
        }

        return [
            'provider_id' => 'mailchimp',
            'title' => __('Marketing', 'griffinforms-form-builder'),
            'provider_label' => $provider_label,
            'provider_icon' => $provider_icon,
            'provider_icon_class' => 'gf-marketing-summary-icon-monochrome',
            'badge_html' => $this->marketingStatusBadge($status),
            'summary' => $this->getMailchimpMarketingSummary($state, $provider_label),
            'meta' => $rows,
        ];
    }

    private function getMailchimpMarketingSummary(array $state, string $provider_label): string
    {
        $status = sanitize_key((string) ($state['status'] ?? ''));
        $email = sanitize_email((string) ($state['subscriber_email'] ?? ''));
        $audience_id = sanitize_text_field((string) ($state['audience_id'] ?? ''));
        $message = sanitize_text_field((string) ($state['message'] ?? ''));

        if ($status === \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_SYNCED) {
            if ($email !== '' && $audience_id !== '') {
                return sprintf(__('%1$s synced %2$s to audience %3$s.', 'griffinforms-form-builder'), $provider_label, $email, $audience_id);
            }

            return sprintf(__('%s sync completed successfully for this submission.', 'griffinforms-form-builder'), $provider_label);
        }

        if ($status === \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_SKIPPED) {
            return $message !== ''
                ? $message
                : sprintf(__('%s did not sync this submission.', 'griffinforms-form-builder'), $provider_label);
        }

        if ($status === \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_FAILED) {
            return sprintf(__('%s could not complete sync for this submission.', 'griffinforms-form-builder'), $provider_label);
        }

        if ($status === \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_PROCESSING) {
            return sprintf(__('%s is currently processing this submission.', 'griffinforms-form-builder'), $provider_label);
        }

        if ($status === \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_QUEUED) {
            return sprintf(__('%s queued this submission for background sync.', 'griffinforms-form-builder'), $provider_label);
        }

        return sprintf(__('%s activity was recorded for this submission.', 'griffinforms-form-builder'), $provider_label);
    }

    private function buildKlaviyoMarketingCard(): ?array
    {
        $meta      = $this->getSubmissionMetaPayload();
        $marketing = isset($meta['marketing']) && is_array($meta['marketing']) ? $meta['marketing'] : [];
        $state     = isset($marketing['klaviyo']) && is_array($marketing['klaviyo']) ? $marketing['klaviyo'] : [];
        if (empty($state)) {
            return null;
        }

        $providers      = Providers::getInstance();
        $provider       = $providers->get('klaviyo', true);
        $provider_label = is_array($provider) ? trim((string) ($provider['name'] ?? 'Klaviyo')) : 'Klaviyo';
        $provider_icon  = is_array($provider)
            ? trim((string) ($provider['icon'] ?? ($provider['icon_dark'] ?? '')))
            : '';

        $status = sanitize_key((string) ($state['status'] ?? ''));

        $rows = [];

        $list_id = sanitize_text_field((string) ($state['list_id'] ?? ''));
        if ($list_id !== '') {
            $rows[] = ['label' => __('List', 'griffinforms-form-builder'), 'value' => $list_id];
        }

        $subscriber_email = sanitize_email((string) ($state['subscriber_email'] ?? ''));
        if ($subscriber_email !== '') {
            $rows[] = ['label' => __('Contact', 'griffinforms-form-builder'), 'value' => $subscriber_email];
        }

        $profile_id = sanitize_text_field((string) ($state['profile_id'] ?? ''));
        if ($profile_id !== '') {
            $rows[] = ['label' => __('Profile ID', 'griffinforms-form-builder'), 'value' => $profile_id];
        }

        if (!empty($state['consent_required'])) {
            $rows[] = [
                'label' => __('Consent', 'griffinforms-form-builder'),
                'value' => !empty($state['consent_granted'])
                    ? __('Required and accepted', 'griffinforms-form-builder')
                    : __('Required but not accepted', 'griffinforms-form-builder'),
            ];
        }

        $custom_properties = isset($state['custom_properties']) && is_array($state['custom_properties'])
            ? $state['custom_properties']
            : [];
        if (!empty($custom_properties)) {
            $formatted = [];
            foreach ($custom_properties as $prop_name => $prop_value) {
                $formatted[] = esc_html(sanitize_text_field((string) $prop_name)) . ': ' . esc_html(sanitize_text_field((string) $prop_value));
            }
            $rows[] = ['label' => __('Custom properties', 'griffinforms-form-builder'), 'value' => implode(', ', $formatted)];
        }

        $reason = sanitize_key((string) ($state['reason'] ?? ''));
        if ($reason !== '') {
            $rows[] = ['label' => __('Skip reason', 'griffinforms-form-builder'), 'value' => $this->formatMarketingStateLabel($reason)];
        }

        $last_synced_at = $this->formatDateTime((string) ($state['last_synced_at'] ?? ''));
        if ($last_synced_at !== '') {
            $rows[] = ['label' => __('Last synced', 'griffinforms-form-builder'), 'value' => $last_synced_at];
        }

        $last_attempted_at = $this->formatDateTime((string) ($state['last_attempted_at'] ?? ''));
        if ($last_attempted_at !== '' && $last_synced_at === '') {
            $rows[] = ['label' => __('Last attempted', 'griffinforms-form-builder'), 'value' => $last_attempted_at];
        }

        $updated_at = $this->formatDateTime((string) ($state['updated_at'] ?? ''));
        if ($updated_at !== '') {
            $rows[] = ['label' => __('State updated', 'griffinforms-form-builder'), 'value' => $updated_at];
        }

        $error = sanitize_text_field((string) ($state['last_error'] ?? ''));
        if ($error !== '' && $status === 'failed') {
            $rows[] = ['label' => __('Last error', 'griffinforms-form-builder'), 'value' => $error];
        }

        return [
            'provider_id'         => 'klaviyo',
            'title'               => __('Marketing', 'griffinforms-form-builder'),
            'provider_label'      => $provider_label,
            'provider_icon'       => $provider_icon,
            'provider_icon_class' => 'gf-marketing-summary-icon-monochrome',
            'badge_html'          => $this->marketingStatusBadge($status),
            'summary'             => $this->getKlaviyoMarketingSummary($state, $provider_label),
            'meta'                => $rows,
        ];
    }

    private function getKlaviyoMarketingSummary(array $state, string $provider_label): string
    {
        $status  = sanitize_key((string) ($state['status'] ?? ''));
        $email   = sanitize_email((string) ($state['subscriber_email'] ?? ''));
        $list_id = sanitize_text_field((string) ($state['list_id'] ?? ''));
        $message = sanitize_text_field((string) ($state['message'] ?? ''));

        if ($status === 'synced') {
            if ($email !== '' && $list_id !== '') {
                return sprintf(
                    /* translators: 1: provider name, 2: email, 3: list ID */
                    __('%1$s contact synced for %2$s to list %3$s.', 'griffinforms-form-builder'),
                    esc_html($provider_label),
                    esc_html($email),
                    esc_html($list_id)
                );
            }
            return sprintf(__('%s contact synced.', 'griffinforms-form-builder'), esc_html($provider_label));
        }

        if ($status === 'skipped') {
            $reason = sanitize_key((string) ($state['reason'] ?? ''));
            if ($reason === 'consent_not_granted') {
                return sprintf(__('%s sync skipped — consent not granted.', 'griffinforms-form-builder'), esc_html($provider_label));
            }
            if ($reason === 'missing_email') {
                return sprintf(__('%s sync skipped — email field was empty.', 'griffinforms-form-builder'), esc_html($provider_label));
            }
            if ($reason === 'form_disabled') {
                return sprintf(__('%s is disabled for this form.', 'griffinforms-form-builder'), esc_html($provider_label));
            }
            return sprintf(__('%s sync skipped.', 'griffinforms-form-builder'), esc_html($provider_label));
        }

        if ($status === 'failed') {
            $error = sanitize_text_field((string) ($state['last_error'] ?? ''));
            if ($error !== '') {
                return sprintf(__('%s sync failed: %s', 'griffinforms-form-builder'), esc_html($provider_label), esc_html($error));
            }
            return sprintf(__('%s sync failed.', 'griffinforms-form-builder'), esc_html($provider_label));
        }

        if ($status === 'queued' || $status === 'processing') {
            return sprintf(__('%s sync is pending.', 'griffinforms-form-builder'), esc_html($provider_label));
        }

        return $message !== ''
            ? esc_html($message)
            : sprintf(__('%s activity recorded.', 'griffinforms-form-builder'), esc_html($provider_label));
    }

    private function marketingStatusBadge(string $status): string
    {
        $normalized = strtolower(trim($status));
        $map = array(
            \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_SYNCED => array('label' => __('Synced', 'griffinforms-form-builder'), 'class' => 'badge bg-success'),
            \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_QUEUED => array('label' => __('Queued', 'griffinforms-form-builder'), 'class' => 'badge bg-info text-dark'),
            \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_PROCESSING => array('label' => __('Processing', 'griffinforms-form-builder'), 'class' => 'badge bg-primary'),
            \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_SKIPPED => array('label' => __('Skipped', 'griffinforms-form-builder'), 'class' => 'badge bg-secondary'),
            \GriffinForms\Includes\Integrations\Mailchimp\Contract::STATUS_FAILED => array('label' => __('Failed', 'griffinforms-form-builder'), 'class' => 'badge bg-danger'),
        );
        $fallback = array('label' => $this->formatMarketingStateLabel($normalized !== '' ? $normalized : 'unknown'), 'class' => 'badge bg-secondary');
        $data = $map[$normalized] ?? $fallback;

        return '<span class="' . esc_attr($data['class']) . '">' . esc_html($data['label']) . '</span>';
    }

    private function formatMarketingStateLabel(string $value): string
    {
        $value = trim($value);
        if ($value === '') {
            return __('Not recorded', 'griffinforms-form-builder');
        }

        return ucwords(str_replace(array('_', '-'), ' ', $value));
    }

    private function getResumeUrl(): string
    {
        if ($this->payment_status !== 'pending' || $this->item_id <= 0) {
            return '';
        }
        $token = '';
        if (!empty($this->payment_meta['retry']['token'])) {
            $token = sanitize_text_field($this->payment_meta['retry']['token']);
        }
        if ($token === '') {
            return '';
        }
        $base = '';
        if (!empty($this->payment_meta['retry']['resume_url'])) {
            $base = esc_url_raw($this->payment_meta['retry']['resume_url']);
        }
        if ($base === '') {
            $base = home_url('/form-page/');
        }
        return add_query_arg(
            array(
                'gf_payment_resume' => $this->item_id . ':' . $token,
            ),
            $base
        );
    }

    private function getSubmissionEditButtonState(): array
    {
        $default = array(
            'allowed' => false,
            'url' => '',
            'message' => __('Submission editing is not available right now.', 'griffinforms-form-builder'),
        );

        if ($this->item_id <= 0 || !$this->is_valid_form) {
            return $default;
        }

        $submission_context = SubmissionContextBuilder::getInstance()->build((int) $this->item_id);
        $submission_meta = isset($submission_context['submission_meta']) && is_array($submission_context['submission_meta'])
            ? $submission_context['submission_meta']
            : array();
        $submission_token = isset($submission_meta['public_token']) ? sanitize_text_field((string) $submission_meta['public_token']) : '';

        if ($submission_token === '') {
            $default['message'] = __('This submission cannot be edited right now.', 'griffinforms-form-builder');
            return $default;
        }

        $guard_manager = new FormGuardManager();
        $result = $guard_manager->evaluate((int) $this->item->getProp('form_id'), array(
            'is_edit' => true,
            'submission_id' => (int) $this->item_id,
            'submission_token' => $submission_token,
            'submission_context' => $submission_context,
        ));

        if (!empty($result['blocked'])) {
            $default['message'] = isset($result['message']) && is_string($result['message']) && $result['message'] !== ''
                ? $result['message']
                : $default['message'];
            return $default;
        }

        $url = PlacementService::getInstance()->getSubmissionEditUrl((int) $this->item_id, $submission_token);
        if ($url === '') {
            $default['message'] = __('This submission cannot be edited right now.', 'griffinforms-form-builder');
            return $default;
        }

        return array(
            'allowed' => true,
            'url' => $url,
            'message' => '',
        );
    }
}
