<?php

namespace GriffinForms\Frontend\Html\Forms;

class Form extends \GriffinForms\Frontend\Html\Forms\Format
{
    use \GriffinForms\Frontend\Html\Forms\Traits\ExternalAssets;
    use \GriffinForms\Frontend\Html\Forms\Traits\SecurityAccess;

    protected $meta;
    protected $item;
    protected $bot_limiter;
    protected $user_action;
    protected $access_alert;
    protected $form_theme;
    protected $theme_missing = false;
    protected $theme_slug = '';
    protected $bs_form_main_container_classes = 'bg-white text-dark p-3 rounded';
    protected $bs_form_header_classes = 'text-dark';
    protected $bs_form_notices_classes = '';
    protected $bs_form_pages_container_classes = '';
    protected $bs_payment_review_container_classes = 'card border-0 shadow-sm p-4 mt-4';
    protected $bs_payment_review_header_classes = '';
    protected $bs_payment_review_heading_classes = 'h5 mb-1';
    protected $bs_payment_review_description_classes = 'text-muted';
    protected $bs_payment_review_summary_classes = 'card border-0 shadow-sm p-4 bg-light';
    protected $bs_payment_review_field_classes = 'border rounded-3 p-3 bg-white shadow-sm';
    protected $bs_payment_review_field_wrapper_classes = 'card border-0 shadow-sm bg-light';
    protected $bs_payment_review_back_btn_classes = 'button button-secondary wp-element-button wp-block-button__link';
    protected $bs_payment_review_continue_btn_classes = 'button button-primary wp-element-button wp-block-button__link';
    protected $requires_payment = false;
    protected $payment_control_tokens = array();
    protected $resume_payment_context = array();
    protected $resume_payment_error = '';
    protected $resume_attempted = false;
    protected $edit_mode = false;
    protected $submission_context = array();
    protected $submission_id = 0;
    protected $submission_token = '';

    protected function setAntiSpam()
    {
        parent::setAntiSpam();
        if (method_exists($this->antispam, 'setFormContext')) {
            $this->antispam->setFormContext($this->id);
        }
    }
    
    public function html()
    {
        if ($this->sql->itemExists('form', $this->id) === false) {
            return $this->alerts->formNotFound();
        }

        $this->applySubmissionEditContextFromRequest();

        if (!$this->hasAccess()) {
            return $this->access_alert;
        }

        $this->trackFrontendRenderUrl();

        $this->setBotLimiter();

        // Preliminary rate limit / blacklist gate (final enforcement is in AJAX handlers)
        if ($this->bot_limiter->isRateLimited()) {
            return $this->alerts->rateLimitReached();
        }

        // Count this view as an event
        // $this->bot_limiter->markEvent();

        $this->externalScripts();

        $guard_manager = new \GriffinForms\Frontend\Security\FormGuardManager();
        $guard_result = $guard_manager->evaluate($this->id, [
            'context' => 'render',
            'is_edit' => $this->edit_mode,
            'submission_id' => $this->submission_id,
            'submission_token' => $this->submission_token,
            'submission_context' => $this->submission_context,
        ]);

        if (!empty($guard_result['blocked'])) {
            $message = isset($guard_result['message']) ? $guard_result['message'] : '';
            if ($guard_result['key'] === 'availability_window') {
                return $this->alerts->availabilityWindow($message);
            }
            if ($guard_result['key'] === 'submission_edit_window') {
                return $this->alerts->submissionEditWindow($message);
            }
            if ($guard_result['key'] === 'limit_entries') {
                return $this->alerts->limitReached($message);
            }
            return $this->alerts->availabilityWindow($message);
        }

        // Load theme CSS if form has a theme assigned
        $theme_class = '';
        $theme_css = $this->getFormThemeCss();
        $theme_assets_markup = $this->getThemeAssetMarkup();
        $is_theme_set = !empty($theme_css);

        // Enqueue theme overrides JavaScript if theme is set
        if ($is_theme_set) {
            $this->enqueueThemeOverrides();
        }

        $resume_context = $this->detectResumePaymentContext();
        if (!empty($resume_context)) {
            $this->resume_payment_context = $resume_context;
            $this->requires_payment = true;
        }

        // Check if theme was assigned but is missing from database
        $form_theme_id = $this->item->getProp('form_theme');
        if (!empty($form_theme_id) && !$is_theme_set) {
            $this->theme_missing = $this->sql->itemExists('formtheme', absint($form_theme_id)) === false;
        }

        if (!$is_theme_set) {
            $theme_class = ' gf-no-theme';
        }

        $theme_identifier_attr = '';
        $theme_slug_class = '';
        if ($is_theme_set && !empty($this->theme_slug)) {
            $theme_identifier_attr = ' data-gf-theme="' . esc_attr($this->theme_slug) . '"';
            $theme_slug_class = ' griffinforms-theme-' . esc_attr($this->theme_slug);
        }

        $edit_mode_attr = $this->edit_mode ? ' data-gf-edit-mode="1"' : ' data-gf-edit-mode="0"';
        $submission_id_attr = $this->submission_id > 0 ? ' data-gf-submission-id="' . absint($this->submission_id) . '"' : '';
        $html = '<div class="griffinforms-form' . absint($this->id) . '-container griffinforms-form-container row' . $theme_class . $theme_slug_class . (!$is_theme_set && !empty($this->bs_form_main_container_classes) ? ' ' . esc_attr($this->bs_form_main_container_classes) : '') . '" data-gf-form-id="' . absint($this->id) . '"' . $theme_identifier_attr . $edit_mode_attr . $submission_id_attr . '>';
        $html .= $theme_assets_markup . $theme_css;
        $html .= '<noscript><div class="griffinforms-nojs-notice alert alert-warning small" role="alert" data-gf-element="nojs-notice">';
        $html .= '<div class="griffinforms-nojs-title fw-medium" data-gf-element="nojs-title">' . esc_html__('JavaScript required', 'griffinforms-form-builder') . '</div>';
        $html .= '<div class="griffinforms-nojs-body" data-gf-element="nojs-body">' . esc_html__('This form needs JavaScript to work. Please enable JavaScript in your browser and reload the page.', 'griffinforms-form-builder') . '</div>';
        $html .= '</div></noscript>';

        if ($this->resume_attempted && !empty($this->resume_payment_error)) {
            $html .= $this->resumeBlockedNotice($is_theme_set);
            $html .= '</div>';
            return $html;
        }

        $html .= $this->formHeader($is_theme_set);
        $html .= $this->formNotices();
        $html .= $this->progressBar();
        $html .= $this->formPages($is_theme_set);
        $html .= '<div class="griffinforms-form-lock" data-gf-form-lock data-gf-form-id="' . absint($this->id) . '" data-gf-element="form-lock" aria-hidden="true" style="display:none;">';
        $html .= '<div class="griffinforms-form-lock-card" data-gf-element="form-lock-card" role="status" aria-live="polite">';
        $html .= '<span class="spinner-border spinner-border-sm" data-gf-element="form-lock-spinner" role="status" aria-hidden="true"></span>';
        $html .= '<span>' . esc_html__('Please wait…', 'griffinforms-form-builder') . '</span>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= $this->successMessage($is_theme_set);
        return $html;
    }

    protected function applySubmissionEditContextFromRequest(): void
    {
        $submission_id = isset($_GET['gf_sub_id']) ? absint(wp_unslash($_GET['gf_sub_id'])) : 0;
        $submission_token = isset($_GET['gf_sub_token']) ? sanitize_text_field(wp_unslash($_GET['gf_sub_token'])) : '';

        if ($submission_id <= 0 || strlen($submission_token) !== 40) {
            return;
        }

        $builder = \GriffinForms\Includes\Pipelines\SubmissionContextBuilder::getInstance();
        $context = $builder->build($submission_id);

        $stored_token = isset($context['submission_meta']['public_token']) ? (string) $context['submission_meta']['public_token'] : '';
        if ($stored_token === '' || !hash_equals($stored_token, $submission_token)) {
            return;
        }

        $this->edit_mode = true;
        $this->submission_context = $context;
        $this->submission_id = $submission_id;
        $this->submission_token = $submission_token;
        $this->data['edit_mode'] = true;
        $this->data['submission_context'] = $context;
        $this->data['submission_id'] = $submission_id;
        $this->data['submission_token'] = $submission_token;
    }

    protected function trackFrontendRenderUrl(): void
    {
        if (is_admin()) {
            return;
        }

        if (isset($_GET['gf_preview_form']) && absint(wp_unslash($_GET['gf_preview_form'])) > 0) {
            return;
        }

        $url = '';
        if (function_exists('get_permalink')) {
            $url = (string) get_permalink();
        }
        if ($url === '' && !empty($_SERVER['REQUEST_URI'])) {
            $url = home_url(wp_unslash((string) $_SERVER['REQUEST_URI']));
        }

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

        $logger = new \GriffinForms\Includes\Publication\FrontendUrlLogger();
        $logger->append($this->id, $url);
    }

    /**
     * Enqueues external assets (CSS, JavaScript, Google Fonts)
     * Asset-specific methods are delegated to ExternalAssets trait
     *
     * NOTE: Bootstrap fallback CSS (gf-no-theme.css) is enqueued in the view layer
     * via Frontend\Views\Format::commonCssFiles() to ensure proper WordPress hook timing.
     */
    public function externalScripts()
    {
        $this->antispam->enqueueCaptchaScripts();
        $this->enqueueThemeCss();
        $this->enqueueThemeJs();
        $this->enqueueGoogleFonts();
    }
    
    protected function formNotices()
    {
        return '<div id="griffinforms-form-alert-danger" class="griffinforms-form-alert alert alert-danger border-0" role="alert" style="display:none;" data-gf-element="error" data-gf-form-id="' . absint($this->id) . '"></div>';
    }

    private function themeMissingAlert()
    {
        $html = '<div class="col-md-12 mb-3">';
        $html .= '<div class="alert alert-warning small" role="alert">';
        $html .= '<strong class="fw-medium">⚠️ ' . esc_html__('Theme Not Found', 'griffinforms-form-builder') . '</strong>';
        $html .= '<p class="mb-0 mt-2">' . esc_html__('The theme assigned to this form no longer exists. The form is being displayed using default styling. Please assign a valid theme or remove the theme assignment.', 'griffinforms-form-builder') . '</p>';
        $html .= '</div>';
        $html .= '</div>';
        return $html;
    }

    private function formHeader($is_theme_set = false)
    {
        $heading = $this->item->getProp('heading');
        $description = $this->item->getProp('description');

        if (empty($heading) && empty($description)) {
            return '';
        }

        $html = '<div class="griffinforms-form-header col-md-12 mb-3' . (!$is_theme_set && !empty($this->bs_form_header_classes) ? ' ' . esc_attr($this->bs_form_header_classes) : '') . '">';
        if (!empty($heading)) {
            $html .= '<div class="h2" data-gf-element="form-title">' . esc_html($heading) . '</div>';
        }
        if (!empty($description)) {
            $html .= '<div data-gf-element="form-description">' . esc_html($description) . '</div>';
        }
        $html .= $this->complianceIpNotice();
        $html .= '</div>';

        return $html;
    }

    private function complianceIpNotice(): string
    {
        if (!$this->shouldShowComplianceIpNotice()) {
            return '';
        }

        $message = __('This form may record your IP address for security and compliance purposes.', 'griffinforms-form-builder');
        $html = '<div class="col-md-12 mb-3">';
        $html .= '<div class="alert alert-info small mt-2" role="note" data-gf-element="compliance-ip-notice">';
        $html .= esc_html($message);
        $html .= '</div>';
        $html .= '</div>';
        return $html;
    }

    private function progressBar(): string
    {
        $progress_bar = $this->item->getProp('progress_bar');
        if (!is_array($progress_bar) || empty($progress_bar['toggle'])) {
            return '';
        }

        $page_ids = $this->item->getProp('formpages');
        if (!is_array($page_ids) || count($page_ids) <= 1) {
            return '';
        }

        $style = isset($progress_bar['style']) && in_array($progress_bar['style'], array('stepper', 'tabs'), true) ? $progress_bar['style'] : 'stepper';
        $show_page_headings = !isset($progress_bar['show_page_headings']) || (bool) $progress_bar['show_page_headings'];
        $page_labels = array();
        $page_icons = array();

        foreach ($page_ids as $index => $page_id) {
            $page_item = new \GriffinForms\Items\FormPage();
            if (!$page_item->loadItem($page_id)) {
                $page_labels[] = sprintf(__('Step %d', 'griffinforms-form-builder'), $index + 1);
                $page_icons[] = '';
                continue;
            }

            $page_labels[] = $this->getProgressStepLabel($page_item, $index + 1, $show_page_headings);
            $page_icons[] = $this->getProgressStepIcon($page_item);
        }

        $html = '<div class="griffinforms-progress-bar" data-gf-progress-bar="true" data-gf-progress-style="' . esc_attr($style) . '" data-gf-progress-active="1" data-gf-progress-total="' . absint(count($page_ids)) . '">';
        $html .= '<div class="griffinforms-progress-bar__track">';
        $html .= '<svg class="griffinforms-progress-bar__connector" aria-hidden="true" focusable="false"></svg>';
        foreach ($page_labels as $index => $label) {
            $step = $index + 1;
            $state = $step === 1 ? 'active' : 'upcoming';
            $icon = $page_icons[$index] ?? '';
            $html .= '<div class="griffinforms-progress-bar__step" data-gf-progress-step="' . absint($step) . '" data-gf-progress-state="' . esc_attr($state) . '">';
            if ($style === 'tabs' && $icon !== '') {
                $html .= $this->getProgressMarkerInnerHtml($step, $icon, $style);
            } else {
                $marker_class = 'griffinforms-progress-bar__marker' . ($icon !== '' ? ' griffinforms-progress-bar__marker--icon' : '');
                $html .= '<span class="' . esc_attr($marker_class) . '" aria-hidden="true">';
                $html .= $this->getProgressMarkerInnerHtml($step, $icon, $style);
                $html .= '<span class="griffinforms-progress-bar__marker-check" aria-hidden="true"></span>';
                $html .= '</span>';
            }
            $html .= '<span class="griffinforms-progress-bar__label">' . esc_html($label) . '</span>';
            $html .= '</div>';
        }
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    private function getProgressStepLabel(\GriffinForms\Items\FormPage $page_item, int $step, bool $show_page_headings): string
    {
        $step_data = $page_item->getProp('step_data');
        if (is_array($step_data)) {
            $step_name = trim((string) ($step_data['step_name'] ?? ''));
            if ($step_name !== '') {
                return $step_name;
            }
        }

        $heading = trim((string) $page_item->getProp('heading'));
        if ($show_page_headings && $heading !== '') {
            return $heading;
        }

        $name = trim((string) $page_item->getProp('name'));
        if ($show_page_headings && $name !== '') {
            return $name;
        }

        return sprintf(__('Step %d', 'griffinforms-form-builder'), $step);
    }

    private function getProgressStepIcon(\GriffinForms\Items\FormPage $page_item): string
    {
        $step_data = $page_item->getProp('step_data');
        if (!is_array($step_data)) {
            return '';
        }

        return trim((string) ($step_data['step_icon'] ?? ''));
    }

    private function getProgressMarkerInnerHtml(int $step, string $icon): string
    {
        if ($icon !== '') {
            return '<span class="material-symbols-outlined griffinforms-progress-bar__marker-icon" aria-hidden="true" style="transform:translateY(0);">' . esc_html($icon) . '</span>';
        }

        return '<span class="griffinforms-progress-bar__marker-number" style="transform:none;">' . absint($step) . '</span>';
    }

    private function shouldShowComplianceIpNotice(): bool
    {
        $settings = \GriffinForms\Settings::getInstance();
        $show_notice = (int) $settings->getOption('compliance_show_ip_notice', 0);
        if ($show_notice !== 1) {
            return false;
        }

        $compliance_mode = (int) $this->item->getProp('compliance_mode');
        if ($compliance_mode !== 1) {
            return false;
        }

        $flags_raw = $this->item->getProp('compliance_flags');
        if (!is_array($flags_raw)) {
            $flags_raw = [];
        }
        $flags = \GriffinForms\Includes\Helpers\Compliance::resolveFlags($flags_raw);

        $defaults_enabled = (int) $settings->getOption('compliance_defaults_enabled', 0);
        $profile = (string) $this->item->getProp('compliance_profile');
        if (!empty($flags['mode']) && $flags['mode'] === 'inherit' && $defaults_enabled === 1) {
            $profile = (string) $settings->getOption('compliance_default_profile', 'standard');
        }

        if ($profile !== 'hipaa') {
            return false;
        }

        return !empty($flags['store_ip']);
    }
    
    private function formPages($is_theme_set = false)
    {
        $page_ids = $this->item->getProp('formpages');
        
        if (empty($page_ids) or !is_array($page_ids)) {
            return;
        }

        $container_classes = 'griffinforms-form-children-container col-md-12';
        if (!$is_theme_set && !empty($this->bs_form_pages_container_classes)) {
            $container_classes .= ' ' . esc_attr($this->bs_form_pages_container_classes);
        }
        $html = '<div class="' . esc_attr($container_classes) . '">';
        
        $valid_page_ids = array();
        
        foreach ($page_ids as $page_id) {

            if (!$this->sql->itemExists('formpage', $page_id)) {
                continue;
            } else {
                $valid_page_ids[] = $page_id;
            }
        }
        
        $this->requires_payment = $this->detectPaymentRequirement($valid_page_ids);
        if ($this->shouldSkipPaymentReviewForPaidEdit()) {
            $this->requires_payment = false;
        }

        foreach ($valid_page_ids as $key => $page_id) {
            $position = 'middle';
            if ($key === 0) {$position = 'first';}
            if ($key === (count($valid_page_ids) - 1)) {$position = 'last';}
            if (count($valid_page_ids) === 1) {$position = 'single';}

            $submit_label = $this->item->getProp('submit_label');
            $form_theme_id = $this->item->getProp('form_theme');

            // Verify theme exists in database before setting is_theme_set to true
            $is_theme_valid = false;
            if (!empty($form_theme_id) && absint($form_theme_id) > 0) {
                $is_theme_valid = $this->sql->itemExists('formtheme', absint($form_theme_id));
            }

            $data = array(
                'position' => $position,
                'form' => $this->id,
                'user_action' => $this->user_action,
                'autocomplete' => $this->item->getProp('autofill'),
                'submit_label' => $submit_label,
                'is_theme_set' => $is_theme_valid,
                'theme_missing' => !empty($form_theme_id) && !$is_theme_valid,
                'requires_payment' => $this->requires_payment,
                'edit_mode' => $this->edit_mode,
                'submission_context' => $this->submission_context,
                'submission_id' => $this->submission_id,
                'submission_token' => $this->submission_token,
            );

            $page = new FormPage($page_id, $data);
            $html .= $page->html();
        }
            
        $html .= '</div>';

        if ($this->requires_payment) {
            $html .= $this->paymentReviewContainer($is_theme_set);
        }

        return $html;
    }
    
    private function detectPaymentRequirement(array $page_ids)
    {
        if (empty($page_ids)) {
            return false;
        }

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

            $row_ids = $page_item->getProp('pagerows');
            if (empty($row_ids) || !is_array($row_ids)) {
                continue;
            }

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

                $column_ids = $row_item->getProp('rowcolumns');
                if (empty($column_ids) || !is_array($column_ids)) {
                    continue;
                }

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

                    $field_ids = $column_item->getProp('fields');
                    if (empty($field_ids) || !is_array($field_ids)) {
                        continue;
                    }

                    foreach ($field_ids as $field_id) {
                        if (!$this->sql->itemExists('field', $field_id)) {
                            continue;
                        }
                        $field_type = strtolower((string) $this->sql->getFieldType($field_id));
                        if ($field_type === 'payment') {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }

    private function shouldSkipPaymentReviewForPaidEdit(): bool
    {
        if (!$this->edit_mode) {
            return false;
        }

        $submission = isset($this->submission_context['submission']) && is_array($this->submission_context['submission'])
            ? $this->submission_context['submission']
            : array();
        $payment_status = isset($submission['payment_status']) ? sanitize_key((string) $submission['payment_status']) : '';

        return $payment_status === 'paid';
    }

    /**
     * Detect a payment resume context from query parameters.
     */
    private function detectResumePaymentContext(): array
    {
        if (!isset($_GET['gf_payment_resume'])) {
            return [];
        }

        $raw = sanitize_text_field(wp_unslash($_GET['gf_payment_resume']));
        $this->resume_attempted = true;
        if (strpos($raw, ':') === false) {
            $this->setResumeError(__('This payment link is invalid.', 'griffinforms-form-builder'));
            return [];
        }

        list($sub_id_part, $token_part) = explode(':', $raw, 2);
        $submission_id = absint($sub_id_part);
        $token = sanitize_text_field($token_part);

        if ($submission_id <= 0 || $token === '') {
            $this->setResumeError(
                __('This payment link is invalid.', 'griffinforms-form-builder'),
                $raw,
                $submission_id
            );
            return [];
        }

        global $wpdb;
        $table = esc_sql($this->config->getTable('submission'));
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $submission_id
            ),
            ARRAY_A
        );

        if (empty($row)) {
            $this->setResumeError(
                __('We could not locate this payment link.', 'griffinforms-form-builder'),
                $raw,
                $submission_id,
                $row
            );
            return [];
        }

        if ((int) ($row['form_id'] ?? 0) !== (int) $this->id) {
            $this->setResumeError(
                __('We could not locate this payment link.', 'griffinforms-form-builder'),
                $raw,
                $submission_id,
                $row
            );
            return [];
        }

        if (!isset($row['payment_status']) || $row['payment_status'] !== 'pending') {
            $this->setResumeError(
                __('This payment has already been processed or is no longer pending.', 'griffinforms-form-builder'),
                $raw,
                $submission_id
            );
            return [];
        }

        $meta_helper = \GriffinForms\Includes\Payments\SubmissionPaymentMeta::getInstance();
        $valid = $meta_helper->validateRetryToken($submission_id, $token);
        if (!$valid) {
            if (isset($this->log)) {
                $meta_snapshot = $meta_helper->getMeta($submission_id);
                $this->log->add(
                    'warning',
                    'submission',
                    $submission_id,
                    get_current_user_id(),
                    sprintf(
                        'Payment resume blocked: retry token mismatch for submission #%d. Provided token "%s"; stored retry bundle: %s',
                        $submission_id,
                        $token,
                        isset($meta_snapshot['retry']) ? wp_json_encode($meta_snapshot['retry']) : 'none'
                    ),
                    'Payment Integration (resume)'
                );
            }
            $this->setResumeError(
                __('This payment link has expired. Please start again.', 'griffinforms-form-builder'),
                $raw,
                $submission_id
            );
            return [];
        }

        $submission_payload = maybe_unserialize($row['submission'] ?? '');
        if (!is_array($submission_payload)) {
            return [];
        }

        $public_token = isset($row['public_token']) ? sanitize_text_field($row['public_token']) : '';
        $cart = \GriffinForms\Includes\Payments\CartBuilder::getInstance()->build($this->id, $submission_payload);
        $resolved_gateway = '';
        if (!empty($cart['selected_gateway'])) {
            $resolved_gateway = sanitize_key($cart['selected_gateway']);
        } else {
            foreach ($submission_payload as $page_data) {
                if (is_array($page_data)) {
                    foreach ($page_data as $field_value) {
                        if (is_array($field_value) && isset($field_value['gateway']) && !empty($field_value['gateway'])) {
                            $resolved_gateway = sanitize_key($field_value['gateway']);
                            break 2;
                        }
                    }
                }
            }
        }

        $registry = \GriffinForms\Includes\Integrations\Categories\Payment::getInstance();
        $gateway_options = [];
        if ($registry) {
            foreach ($registry->getAllowedProviderIds() as $provider_id) {
                $providers = $registry->getProviders();
                if (!isset($providers[$provider_id])) {
                    continue;
                }
                $gateway_options[] = [
                    'id'          => sanitize_key($provider_id),
                    'label'       => sanitize_text_field($providers[$provider_id]['name'] ?? $provider_id),
                    'description' => sanitize_textarea_field($providers[$provider_id]['description'] ?? ''),
                    'icon'        => esc_url($providers[$provider_id]['icon'] ?? ''),
                    'hide_logo'   => false,
                ];
            }
        }

        return [
            'submission_id' => $submission_id,
            'token'         => $token,
            'public_token'  => $public_token,
            'cart'          => $cart,
            'gateways'      => $gateway_options,
            'default_gateway' => $resolved_gateway,
        ];
    }

    /**
     * Record a resume payment error for display and logging.
     */
    private function setResumeError(string $message, string $raw_param = '', int $submission_id = 0, array $row = array()): void
    {
        $this->resume_payment_error = $message;
        if (isset($this->log)) {
            $context_url = $this->currentRequestUrl();
            $param_text = $raw_param !== '' ? 'gf_payment_resume=' . $raw_param : '';
            $detail = sprintf(
                'Payment resume blocked: %s | URL: %s%s',
                $message,
                $context_url,
                $param_text ? ' | Query: ' . $param_text : ''
            );
            $detail .= ' | Current form_id=' . (int) $this->id;
            if (!empty($row)) {
                $detail .= ' | Row form_id=' . (int) ($row['form_id'] ?? 0) . ' submission_id=' . (int) ($row['id'] ?? 0);
            }
            $this->log->add(
                'warning',
                'submission',
                $submission_id > 0 ? $submission_id : $this->id,
                get_current_user_id(),
                $detail,
                'Payment Integration (resume)'
            );
        }
    }

    /**
     * Render a friendly notice when resume is blocked.
     */
    private function resumeBlockedNotice(bool $is_theme_set = false): string
    {
        $container_classes = 'col-12';
        $card_classes = 'p-4 border rounded shadow-sm bg-white';
        $title_class = 'h5 mb-2';
        $desc_class = 'text-muted';

        $html  = '<div class="' . esc_attr($container_classes) . '">';
        $html .= '<div class="' . esc_attr($card_classes) . '">';
        $html .= '<div class="' . esc_attr($title_class) . '">' . esc_html__('Payment link unavailable', 'griffinforms-form-builder') . '</div>';
        $html .= '<p class="' . esc_attr($desc_class) . '">' . esc_html($this->resume_payment_error) . '</p>';
        $html .= '</div>';
        $html .= '</div>';
        return $html;
    }

    /**
     * Best-effort current URL for logging.
     */
    private function currentRequestUrl(): string
    {
        $scheme = is_ssl() ? 'https://' : 'http://';
        $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
        $uri  = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
        $url  = $scheme . $host . $uri;
        return esc_url_raw($url);
    }

    private function resolvePaymentStyleTokens(bool $is_theme_set): array
    {
        $defaults = array(
            'color'             => '#2d3748',
            'font_family'       => "system-ui, -apple-system, 'Segoe UI', sans-serif",
            'font_size'         => '16px',
            'line_height'       => '1.5',
            'background_color'  => '#ffffff',
            'border_color'      => '#ced4da',
            'border_radius'     => '0.375rem',
            'padding'           => '0.75rem 1rem',
            'placeholder_color' => '#6c757d',
        );

        if ($is_theme_set && !empty($this->payment_control_tokens)) {
            foreach ($this->payment_control_tokens as $key => $value) {
                if ($value === '' || $value === null) {
                    continue;
                }
                $defaults[$key] = is_string($value) ? $value : $defaults[$key];
            }
        }

        return $defaults;
    }

    private function paymentReviewContainer($is_theme_set = false)
    {
        $payment_style_tokens = $this->resolvePaymentStyleTokens($is_theme_set);
        $encoded_style_tokens = esc_attr(wp_json_encode($payment_style_tokens));
        $payment_nonce = wp_create_nonce('griffinforms_payment_flow');
        $container_classes = 'griffinforms-payment-review-container griffinforms-formpage-container col-md-12 mt-4';
        if (!$is_theme_set && !empty($this->bs_payment_review_container_classes)) {
            $container_classes .= ' ' . esc_attr($this->bs_payment_review_container_classes);
        }

        $header_classes = 'griffinforms-payment-review-header mb-3';
        if (!$is_theme_set && !empty($this->bs_payment_review_header_classes)) {
            $header_classes .= ' ' . esc_attr($this->bs_payment_review_header_classes);
        }

        $heading_classes = 'griffinforms-payment-review-title';
        if (!$is_theme_set && !empty($this->bs_payment_review_heading_classes)) {
            $heading_classes .= ' ' . esc_attr($this->bs_payment_review_heading_classes);
        }

        $description_classes = 'griffinforms-payment-review-description mb-0';
        if (!$is_theme_set && !empty($this->bs_payment_review_description_classes)) {
            $description_classes .= ' ' . esc_attr($this->bs_payment_review_description_classes);
        }

        $summary_classes = 'griffinforms-payment-review-summary p-3';
        if (!$is_theme_set && !empty($this->bs_payment_review_summary_classes)) {
            $summary_classes .= ' ' . esc_attr($this->bs_payment_review_summary_classes);
        }

        $field_wrapper_classes = 'griffinforms-payment-review-field p-3';
        if (!$is_theme_set && !empty($this->bs_payment_review_field_classes)) {
            $field_wrapper_classes .= ' ' . esc_attr($this->bs_payment_review_field_classes);
        }
        $field_wrapper_fallback = '';
        if (!$is_theme_set && !empty($this->bs_payment_review_field_wrapper_classes)) {
            $field_wrapper_fallback = esc_attr($this->bs_payment_review_field_wrapper_classes);
        }
        $back_btn_classes = 'griffinforms-payment-review-btn griffinforms-payment-review-btn-back';
        if (!$is_theme_set && !empty($this->bs_payment_review_back_btn_classes)) {
            $back_btn_classes .= ' ' . esc_attr($this->bs_payment_review_back_btn_classes);
        }

        $continue_btn_classes = 'griffinforms-payment-review-btn griffinforms-payment-review-btn-continue';
        if (!$is_theme_set && !empty($this->bs_payment_review_continue_btn_classes)) {
            $continue_btn_classes .= ' ' . esc_attr($this->bs_payment_review_continue_btn_classes);
        }

        $resume_mode = !empty($this->resume_payment_context);
        $resume_attrs = '';
        if ($resume_mode) {
            $resume_attrs .= ' data-gf-resume-mode="1"';
            $resume_attrs .= ' data-gf-resume-cart=\'' . esc_attr(wp_json_encode($this->resume_payment_context['cart'])) . '\'';
            $resume_attrs .= ' data-gf-resume-gateways=\'' . esc_attr(wp_json_encode($this->resume_payment_context['gateways'])) . '\'';
            $resume_attrs .= ' data-gf-resume-submission="' . absint($this->resume_payment_context['submission_id']) . '"';
            $resume_attrs .= ' data-gf-resume-token="' . esc_attr($this->resume_payment_context['token']) . '"';
            if (!empty($this->resume_payment_context['default_gateway'])) {
                $resume_attrs .= ' data-gf-resume-default-gateway="' . esc_attr($this->resume_payment_context['default_gateway']) . '"';
            }
            if (!empty($this->resume_payment_context['public_token'])) {
                $resume_attrs .= ' data-gf-resume-sub-token="' . esc_attr($this->resume_payment_context['public_token']) . '"';
            }
        }

        $html  = '<div class="' . esc_attr($container_classes) . '" data-gf-form-id="' . absint($this->id) . '" data-gf-page-type="page" data-gf-page-role="review" data-gf-review-field-class="' . esc_attr($field_wrapper_classes) . '" data-gf-review-field-fallback="' . esc_attr($field_wrapper_fallback) . '" data-gf-payment-nonce="' . esc_attr($payment_nonce) . '" data-gf-payment-style="' . $encoded_style_tokens . '"' . $resume_attrs . ' style="display:none;">';
        $html .= '<div class="' . esc_attr($header_classes) . '">';
        $html .= '<h3 class="' . esc_attr($heading_classes) . '">' . esc_html__('Review & Pay', 'griffinforms-form-builder') . '</h3>';
        $html .= '<p class="' . esc_attr($description_classes) . '">' . esc_html__('Please review your selections before continuing to payment.', 'griffinforms-form-builder') . '</p>';
        $html .= '</div>';
        $html .= '<div class="' . esc_attr($summary_classes) . '" data-gf-review-summary>';
        $html .= '<p class="text-muted mb-0">' . esc_html__('Your payment summary will appear here once available.', 'griffinforms-form-builder') . '</p>';
        $html .= '</div>';
        $html .= '<div class="griffinforms-payment-review-gateway-ui mt-3" data-gf-gateway-ui style="display:none;"></div>';
        $html .= '<div class="griffinforms-payment-review-actions mt-4 d-flex flex-wrap gap-3 justify-content-between">';
        $html .= '<button type="button" class="' . esc_attr($back_btn_classes) . '" data-gf-review-action="back" data-gf-button-type="review-back">';
        $html .= esc_html__('Back to form', 'griffinforms-form-builder');
        $html .= '</button>';
        $html .= '<button type="button" class="' . esc_attr($continue_btn_classes) . '" data-gf-review-action="continue" data-gf-button-type="review-continue">';
        $html .= '<span class="spinner-border spinner-border-sm visually-hidden me-2" role="status" aria-hidden="true"></span>';
        $html .= esc_html__('Continue to payment', 'griffinforms-form-builder');
        $html .= '</button>';
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }
    
/*     private function pagePosition($page_ids, $page_id)
    {
        $total_pages = count($page_ids);
        
        if ($total_pages === 1) {
            return 'single';
        }
    } */
    
    private function successMessage($is_theme_set = false)
    {
        $base_class = 'griffinforms-form' . absint($this->id) . '-success_message griffinforms-form-submitted-success_message mt-2';
        $fallback_classes = isset($this->bs_success_message_classes) ? $this->bs_success_message_classes : 'alert alert-success border-0 small fw-medium p-3';
        $theme_classes = $is_theme_set ? $base_class : $base_class . ' ' . $fallback_classes;
        $style_attr = 'display:none;';

        $form_id_attr = ' data-gf-form-id="' . absint($this->id) . '"';

        if (isset($this->user_action['type']) && $this->user_action['type'] === 'show_success_message') {
            $html = '<div class="' . esc_attr($theme_classes) . '" style="' . esc_attr($style_attr) . '" data-gf-element="success-message"' . $form_id_attr . '>'; 
            $html .= esc_html($this->user_action['message']);
            $html .= '</div>';
            return $html;
        }

        $html = '<div class="' . esc_attr($theme_classes) . '" style="' . esc_attr($style_attr) . '" role="alert" data-gf-element="success-message"' . $form_id_attr . '>';
        $html .= esc_html__('Your response has been recorded.', 'griffinforms-form-builder');
        $html .= '</div>';
        return $html;
    }

    /**
     * Generate and return CSS style tag for form theme
     *
     * Loads form theme if assigned and generates scoped CSS using ThemeCssGen.
     * Returns empty string if no theme is assigned (form falls back to Bootstrap).
     *
     * @return string CSS wrapped in <style> tag or empty string
     */
    private function getFormThemeCss()
    {
        $theme_id = $this->item->getProp('form_theme');

        // If no theme assigned, return empty string (fallback to Bootstrap)
        if (empty($theme_id)) {
            return '';
        }

        // Load the theme object
        $this->form_theme = new \GriffinForms\Items\FormTheme();
        $loaded = $this->form_theme->loadItem($theme_id);

        if (!$loaded) {
            $this->log->add(
                'warning',
                'form',
                $this->id,
                get_current_user_id(),
                sprintf('Theme ID %d failed to load; falling back to default styling.', $theme_id),
                'theme_css'
            );
            return '';
        }

        $this->theme_slug = $this->form_theme->getSlug();

        // Generate CSS using ThemeCssGen
        try {
            $theme_css_gen = new \GriffinForms\Frontend\Html\Themes\ThemeCssGen($this->form_theme, $this->id);
            $css_content = $theme_css_gen->generateAllCss();
            $this->payment_control_tokens = $theme_css_gen->getPaymentControlTokens();
        } catch (Exception $e) {
            $this->log->add(
                'error',
                'form',
                $this->id,
                get_current_user_id(),
                sprintf('Theme CSS generation failed: %s', $e->getMessage()),
                'theme_css'
            );
            return '';
        }

        $css_content = \GriffinForms\Items\FormTheme::prepareStyleTagContent($css_content);
        if (empty($css_content)) {
            $this->log->add(
                'warning',
                'form',
                $this->id,
                get_current_user_id(),
                'Theme CSS generator returned empty output.',
                'theme_css'
            );
            return '';
        }

        // Wrap CSS in style tag
        $output = '<style id="gf-theme-' . absint($this->id) . '">' . "\n" . $css_content . "\n" . '</style>' . "\n";
        return $output;
    }

    private function getThemeAssetMarkup()
    {
        if (empty($this->form_theme)) {
            return '';
        }

        $theme_name = $this->form_theme->getProp('name');
        if (empty($theme_name)) {
            return '';
        }

        $filename = strtolower(str_replace(' ', '_', trim($theme_name)));
        $root_url = $this->config->getRootUrl();
        $root_path = $this->config->getRootPath();
        $markup = '';

        $css_path = $root_path . 'frontend/css/themes/' . $filename . '.css';
        if (file_exists($css_path) && filesize($css_path) > 0) {
            $css_url = $root_url . 'frontend/css/themes/' . $filename . '.css?v=' . filemtime($css_path);
            $markup .= '<link rel="stylesheet" href="' . esc_url($css_url) . '" />';
        } else {
            $this->log->add(
                'notice',
                'form',
                $this->id,
                get_current_user_id(),
                sprintf('Theme asset missing or empty: %s', $css_path),
                'theme_assets'
            );
        }

        $js_path = $root_path . 'frontend/js/themes/' . $filename . '.js';
        if (file_exists($js_path) && filesize($js_path) > 0) {
            $js_url = $root_url . 'frontend/js/themes/' . $filename . '.js?v=' . filemtime($js_path);
            $markup .= '<script src="' . esc_url($js_url) . '" defer></script>';
        } else {
            $this->log->add(
                'notice',
                'form',
                $this->id,
                get_current_user_id(),
                sprintf('Theme asset missing or empty: %s', $js_path),
                'theme_assets'
            );
        }

        return $markup;
    }

    /**
     * Enqueue theme overrides CSS and JavaScript
     *
     * Loads the theme overrides files that apply styling to themed form elements:
     * - CSS: Base styles for input-groups and other themed components
     * - JS: Dynamic styling for pseudo-elements and complex components
     *
     * @return void
     */
    private function enqueueThemeOverrides()
    {
        $root_url = $this->config->getRootUrl();
        $theme_overrides_css = $root_url . 'frontend/css/themes/overrides.css';
        $theme_overrides_js = $root_url . 'frontend/js/themes/overrides.js';

        // Enqueue CSS
        if (!wp_style_is('griffinforms-theme-overrides', 'enqueued')) {
            wp_enqueue_style('griffinforms-theme-overrides', $theme_overrides_css, [], '1.0');
        }

        // Enqueue JavaScript
        if (!wp_script_is('griffinforms-theme-overrides', 'enqueued')) {
            wp_enqueue_script('griffinforms-theme-overrides', $theme_overrides_js, ['jquery'], '1.0', true);
        }
    }

    public function itemJs()
    {
        $form_id = $this->id;
        $gf_sub_id = isset($_GET['gf_sub_id']) ? absint($_GET['gf_sub_id']) : '';
        $gf_sub_token = isset($_GET['gf_sub_token']) ? sanitize_key($_GET['gf_sub_token']) : '';
        $gf_edit_session = $gf_sub_id > 0 && strlen($gf_sub_token) === 40;

        // Loads conditional logic JS for frontend
        $gf_rules = \GriffinForms\Frontend\Html\Rules\FormRules::getInstance();
        $gf_rules->setForm($this->item);
        $gf_rules->rulesJs();

        // Buffer the JS code as a string
        ob_start();
        ?>
        jQuery(document).ready(function($) {
            let formDataName = "gfForm<?php echo absint($form_id); ?>";
            let gfSubId = "<?php echo esc_js($gf_sub_id); ?>";
            let gfSubToken = "<?php echo esc_js($gf_sub_token); ?>";
            let gfEditSession = <?php echo $gf_edit_session ? 'true' : 'false'; ?>;

            const gfFormData = {"subId": gfSubId, "subToken": gfSubToken, "editSession": gfEditSession};
            sessionStorage.setItem(formDataName, JSON.stringify(gfFormData));
        });
        <?php
    
        $inline_js = ob_get_clean();
    
        wp_add_inline_script('griffinforms-frontend', $inline_js);
    }
}
