<?php

namespace GriffinForms\Includes\Migration\Sources;

use GriffinForms\Includes\Helpers\Compliance;
use GriffinForms\Includes\ImportExport\FormExport;
use GriffinForms\Includes\ImportExport\FormImport;
use GriffinForms\Includes\Migration\AbstractMigrationSource;
use GriffinForms\Includes\Migration\MigrationResult;
use GriffinForms\Includes\Pipelines\SubmissionHistoryManager;

class FormidableFormsSource extends AbstractMigrationSource
{
    protected string $slug = 'formidable-forms';
    protected string $label = 'Formidable Forms';

    public function isInstalled(): bool
    {
        global $wpdb;

        $cache_key = 'griffinforms_migration_installed_formidable';
        $cached = get_transient($cache_key);
        if ($cached !== false) {
            return (bool) $cached;
        }

        $table = $wpdb->prefix . 'frm_forms';
        $result = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) === $table;
        set_transient($cache_key, (int) $result, HOUR_IN_SECONDS);

        return $result;
    }

    public function getForms(): array
    {
        global $wpdb;

        if (!$this->isInstalled()) {
            return [];
        }

        $forms = $wpdb->get_results(
            "SELECT f.id, f.name, COUNT(fi.id) AS field_count
             FROM {$wpdb->prefix}frm_forms f
             LEFT JOIN {$wpdb->prefix}frm_fields fi ON fi.form_id = f.id
             WHERE f.status = 'published'
             GROUP BY f.id, f.name
             ORDER BY f.id ASC",
            ARRAY_A
        );

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

        $items = [];

        foreach ($forms as $form) {
            $source_id = absint($form['id'] ?? 0);
            if ($source_id <= 0) {
                continue;
            }

            $items[] = [
                'source_id' => $source_id,
                'title' => sanitize_text_field((string) ($form['name'] ?? '')),
                'field_count' => (int) ($form['field_count'] ?? 0),
            ];
        }

        return $items;
    }

    public function importForm($source_id, array $options = []): MigrationResult
    {
        global $wpdb;

        $result = new MigrationResult();
        $source_id = absint($source_id);

        if ($source_id <= 0) {
            return $result->addError(__('Invalid Formidable form ID.', 'griffinforms-form-builder'));
        }

        $form = $wpdb->get_row(
            $wpdb->prepare("SELECT id, name, description, options FROM {$wpdb->prefix}frm_forms WHERE id = %d AND status = 'published' LIMIT 1", $source_id),
            ARRAY_A
        );

        if (!is_array($form)) {
            return $result->addError(__('Formidable form not found.', 'griffinforms-form-builder'));
        }

        $fields = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, form_id, field_key, name, type, default_value, description, field_order, required, options, field_options
                 FROM {$wpdb->prefix}frm_fields WHERE form_id = %d ORDER BY field_order ASC, id ASC",
                $source_id
            ),
            ARRAY_A
        );

        if (!is_array($fields)) {
            $fields = [];
        }

        $field_entries = [];
        $field_trace_order = [];

        foreach ($fields as $field) {
            $mapped_fields = $this->mapField($field, $result);
            if (empty($mapped_fields)) {
                continue;
            }

            foreach ($mapped_fields as $mapped_field) {
                $field_entries[] = $mapped_field;
                $field_trace_order[] = [
                    'source_field_id' => absint($field['id'] ?? 0),
                ];
            }
        }

        if (empty($field_entries)) {
            return $result->addError(__('No Formidable fields could be mapped into GriffinForms.', 'griffinforms-form-builder'));
        }

        $form_options = $this->decodeMaybeSerialized($form['options'] ?? '');
        $payload = $this->buildFormImportPayload(
            sanitize_text_field((string) ($form['name'] ?? '')),
            sanitize_textarea_field((string) ($form['description'] ?? '')),
            $field_entries
        );

        $importer = new FormImport($payload);
        $griffinforms_form_id = $importer->importForm(false);

        if (!$griffinforms_form_id) {
            return $result->addError(__('GriffinForms could not import the Formidable form.', 'griffinforms-form-builder'));
        }

        $field_id_map = $this->buildFieldIdMap((int) $griffinforms_form_id, $field_trace_order);
        $result->field_id_map = $field_id_map;
        $this->finalizeImportedForm((int) $griffinforms_form_id, $source_id, (string) ($form['name'] ?? ''));

        foreach ($field_entries as $field_entry) {
            $result->addMigrated((string) ($field_entry['heading'] ?? ''), (string) ($field_entry['field_type'] ?? ''));
        }

        if ($this->detectConditionalLogic(is_array($form_options) ? $form_options : [], $fields)) {
            $result->addSkipped(__('Conditional logic', 'griffinforms-form-builder'), __('Conditional logic not migrated.', 'griffinforms-form-builder'));
        }

        return $result->setGriffinFormsFormId((int) $griffinforms_form_id);
    }

    public function supportsSubmissions(): bool
    {
        global $wpdb;

        $table = $wpdb->prefix . 'frm_items';
        $found = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));

        return $found === $table;
    }

    public function importSubmissions($source_form_id, int $griffinforms_form_id, array $options = []): MigrationResult
    {
        global $wpdb;

        $result = new MigrationResult();
        $source_form_id = absint($source_form_id);
        $griffinforms_form_id = absint($griffinforms_form_id);

        if ($source_form_id <= 0 || $griffinforms_form_id <= 0) {
            return $result->addError(__('Invalid Formidable submission migration request.', 'griffinforms-form-builder'));
        }

        if (!$this->supportsSubmissions()) {
            return $result->addError(__('Formidable entry storage is not available on this site.', 'griffinforms-form-builder'));
        }

        $field_id_map = is_array($options['field_id_map'] ?? null) ? $options['field_id_map'] : [];

        if (empty($field_id_map)) {
            return $result->addError(__('Formidable field mapping is missing for submission migration.', 'griffinforms-form-builder'));
        }

        $entries = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, created_at, is_draft FROM {$wpdb->prefix}frm_items WHERE form_id = %d ORDER BY created_at ASC",
                $source_form_id
            ),
            ARRAY_A
        );

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

        $form_export = new FormExport($griffinforms_form_id);
        $form_data = $form_export->getFormData();
        $imported_field_map = $this->buildImportedFieldMap(is_array($form_data) ? $form_data : []);
        $form_row = $wpdb->get_row($wpdb->prepare("SELECT * FROM %i WHERE id = %d LIMIT 1", $this->config->getTable('form'), $griffinforms_form_id), ARRAY_A) ?: [];
        $migrated_count = 0;

        foreach ($entries as $entry) {
            $entry_id = absint($entry['id'] ?? 0);
            if ($entry_id <= 0) {
                continue;
            }

            if ((int) ($entry['is_draft'] ?? 0) === 1) {
                $result->addSkipped((string) $entry_id, __('Draft Formidable entries are not migrated.', 'griffinforms-form-builder'));
                continue;
            }

            $submission_payload = $this->buildSubmissionPayload($entry_id, $field_id_map, $imported_field_map, $result);
            if (empty($submission_payload)) {
                continue;
            }

            $created_utc = sanitize_text_field((string) ($entry['created_at'] ?? ''));
            if ($created_utc === '') {
                $created_utc = current_time('mysql', true);
            }

            $submission_id = $this->insertSubmission($griffinforms_form_id, $submission_payload, $form_row, $created_utc);
            if ($submission_id <= 0) {
                $result->addError(__('A Formidable entry could not be written into GriffinForms.', 'griffinforms-form-builder'));
                continue;
            }

            $result->addMigrated((string) $entry_id, 'submission');
            $migrated_count++;
        }

        if ($migrated_count > 0) {
            $this->incrementLifetimeSubmissionCount($griffinforms_form_id, $migrated_count);
        }

        return $result;
    }

    /**
     * @param array<string, mixed> $field
     * @return array<string, mixed>
     */
    private function mapField(array $field, MigrationResult $result): array
    {
        $type = sanitize_key((string) ($field['type'] ?? ''));
        $label = sanitize_text_field((string) ($field['name'] ?? $type));
        $name = sanitize_text_field((string) ($field['field_key'] ?? $label));
        $is_required = !empty($field['required']) ? 1 : 0;
        $field_options_meta = $this->decodeMaybeSerialized($field['field_options'] ?? '');
        $raw_placeholder = $field_options_meta['placeholder'] ?? '';
        $placeholder = sanitize_text_field(is_array($raw_placeholder) ? '' : (string) $raw_placeholder);
        $description = sanitize_textarea_field((string) ($field['description'] ?? ''));
        $raw_default = $this->decodeMaybeSerialized($field['default_value'] ?? '');
        $default_value = is_array($raw_default) ? '' : sanitize_text_field((string) $raw_default);

        $type_map = [
            'text' => 'inputtext',
            'first_name' => 'inputtext',
            'last_name' => 'inputtext',
            'user_id' => 'inputtext',
            'address' => 'inputaddress',
            'email' => 'inputemail',
            'url' => 'inputurl',
            'textarea' => 'textarea',
            'select' => 'selectfield',
            'radio' => 'inputradio',
            'checkbox' => 'inputcheckbox',
            'number' => 'inputnumber',
            'phone' => 'inputphone',
            'product' => 'payment',
        ];

        if ($type === 'name') {
            $result->addSkipped($label, __('Compound name field split into First Name and Last Name.', 'griffinforms-form-builder'));
            return $this->buildNameFields($label, $name, $is_required, $description);
        }

        if (!isset($type_map[$type])) {
            $reason_map = [
                'hidden' => __('No GriffinForms hidden field equivalent is available in this release.', 'griffinforms-form-builder'),
                'html' => __('Static content block has no GriffinForms equivalent.', 'griffinforms-form-builder'),
                'break' => __('Page break has no direct GriffinForms migration in this release.', 'griffinforms-form-builder'),
                'end_divider' => __('Section end marker has no GriffinForms equivalent.', 'griffinforms-form-builder'),
                'submit' => __('GriffinForms uses its own submit button.', 'griffinforms-form-builder'),
                'captcha' => __('GriffinForms uses its own CAPTCHA system.', 'griffinforms-form-builder'),
                'quantity' => __('Quantity field is linked to a product field. Set quantity options on the Payment field in GriffinForms.', 'griffinforms-form-builder'),
                'total' => __('Calculated total display has no GriffinForms standalone equivalent.', 'griffinforms-form-builder'),
                'credit_card' => __('GriffinForms handles payment collection through its own secure payment field — add a Payment field to your migrated form to collect card details.', 'griffinforms-form-builder'),
            ];

            $result->addSkipped($label, $reason_map[$type] ?? __('Formidable Pro field type or unsupported free field not migrated.', 'griffinforms-form-builder'));
            return [];
        }

        $field_options = [];
        $field_settings = [];
        if ($is_required) {
            $field_settings['required'] = '1';
        }

        $raw_options = $this->decodeMaybeSerialized($field['options'] ?? '');

        if ($type === 'product') {
            $products = [];
            foreach ((array) $raw_options as $option) {
                if (!is_array($option)) {
                    continue;
                }
                $choice_label = sanitize_text_field((string) ($option['label'] ?? ''));
                if ($choice_label === '') {
                    continue;
                }
                $price = (float) preg_replace('/[^\d.]/', '', (string) ($option['price'] ?? '0'));
                $product_id = 'prod_' . substr(md5($choice_label . $price), 0, 6);
                $products[] = [
                    'id' => $product_id,
                    'name' => $choice_label,
                    'price' => $price,
                    'description' => '',
                    'image' => '',
                    'link' => '',
                    'default_qty' => 1,
                    'min_qty' => 1,
                    'max_qty' => 1,
                    'allow_quantity' => 0,
                    'featured' => 0,
                ];
            }
            $field_options = $products;
        } elseif ($type === 'select') {
            $field_options = $this->buildSelectOptions($raw_options);
        } elseif ($type === 'radio' || $type === 'checkbox') {
            $field_options = $this->buildChoiceOptions($raw_options);
        } elseif ($type === 'address') {
            $field_settings['labels'] = [
                'line1' => $label !== '' ? $label : __('Address Line 1', 'griffinforms-form-builder'),
                'line2' => __('Address Line 2', 'griffinforms-form-builder'),
                'city' => __('City', 'griffinforms-form-builder'),
                'state' => __('State / Province', 'griffinforms-form-builder'),
                'postal' => __('Postal / ZIP', 'griffinforms-form-builder'),
                'country' => __('Country', 'griffinforms-form-builder'),
            ];
        }

        $resolved_default_value = $type === 'user_id'
            ? ['value_type' => 'variable', 'variable' => 'user_id', 'custom' => '', 'get_value' => 0, 'get_name' => '']
            : $default_value;

        return [[
            'field_type' => $type_map[$type],
            'name' => $name,
            'heading' => $label,
            'description' => $description,
            'is_required' => $is_required,
            'is_hidden' => $type === 'user_id' ? 1 : 0,
            'placeholder' => $placeholder,
            'default_value' => $resolved_default_value,
            'field_options' => $field_options,
            'field_settings' => $field_settings,
            'validations' => [],
        ]];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function buildNameFields(string $label, string $name, int $is_required, string $description): array
    {
        return [
            [
                'field_type' => 'inputtext',
                'name' => $name . ' First',
                'heading' => $label . ' - ' . __('First Name', 'griffinforms-form-builder'),
                'description' => $description,
                'is_required' => $is_required,
                'is_hidden' => 0,
                'placeholder' => '',
                'default_value' => '',
                'field_options' => [],
                'field_settings' => [],
                'validations' => [],
            ],
            [
                'field_type' => 'inputtext',
                'name' => $name . ' Last',
                'heading' => $label . ' - ' . __('Last Name', 'griffinforms-form-builder'),
                'description' => $description,
                'is_required' => $is_required,
                'is_hidden' => 0,
                'placeholder' => '',
                'default_value' => '',
                'field_options' => [],
                'field_settings' => [],
                'validations' => [],
            ],
        ];
    }

    /**
     * @param mixed $raw_options
     * @return array<string, array<string, string>>
     */
    private function buildSelectOptions($raw_options): array
    {
        $group = [];
        foreach ($this->normalizeChoiceOptions($raw_options) as $option) {
            $label = sanitize_text_field($option);
            if ($label === '') {
                continue;
            }
            $group[$label] = $label;
        }

        return ['' => $group];
    }

    /**
     * @param mixed $raw_options
     * @return array<int, array<string, string>>
     */
    private function buildChoiceOptions($raw_options): array
    {
        $items = [];
        foreach ($this->normalizeChoiceOptions($raw_options) as $option) {
            $label = sanitize_text_field($option);
            if ($label === '') {
                continue;
            }
            $items[] = [$label => $label];
        }

        return $items;
    }

    /**
     * @param mixed $raw_options
     * @return array<int, string>
     */
    private function normalizeChoiceOptions($raw_options): array
    {
        $options = $this->decodeMaybeSerialized($raw_options);
        if (!is_array($options)) {
            return [];
        }

        $normalized = [];
        foreach ($options as $key => $value) {
            if (is_array($value) && isset($value['label'])) {
                $normalized[] = (string) $value['label'];
            } elseif (is_scalar($value)) {
                $normalized[] = (string) $value;
            } elseif (is_string($key) && $key !== '') {
                $normalized[] = $key;
            }
        }

        return $normalized;
    }

    /**
     * @param mixed $value
     * @return mixed
     */
    private function decodeMaybeSerialized($value)
    {
        if (is_array($value) || is_object($value)) {
            return $value;
        }

        if (is_serialized((string) $value)) {
            return maybe_unserialize($value);
        }

        $trimmed = trim((string) $value);
        if ($trimmed !== '' && (($trimmed[0] === '{' && str_ends_with($trimmed, '}')) || ($trimmed[0] === '[' && str_ends_with($trimmed, ']')))) {
            $decoded = json_decode($trimmed, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                return $decoded;
            }
        }

        return $value;
    }

    /**
     * @param array<int, array<string, mixed>> $field_entries
     * @return array<string, mixed>
     */
    private function buildFormImportPayload(string $title, string $description, array $field_entries): array
    {
        $rows = [];

        foreach ($field_entries as $field_entry) {
            $rows[] = [
                'name' => sanitize_text_field((string) ($field_entry['heading'] ?? 'Row')),
                'heading' => '',
                'description' => '',
                'columns' => [
                    [
                        'name' => sanitize_text_field((string) ($field_entry['heading'] ?? 'Column')),
                        'heading' => '',
                        'description' => '',
                        'width' => 12,
                        'field_data' => [$field_entry],
                    ],
                ],
            ];
        }

        return [
            'name' => $title,
            'heading' => $title,
            'description' => $description,
            'autofill' => 0,
            'limit_entries' => [],
            'progress_bar' => [],
            'access_level' => [],
            'submission_edit' => [],
            'user_action' => [
                'type' => 'do_nothing',
                'message' => '',
                'url' => '',
            ],
            'alert_emails' => [get_option('admin_email')],
            'save_entries' => 'always',
            'autoresponder' => '',
            'integration_settings' => [],
            'pages' => [
                [
                    'name' => $title,
                    'heading' => $title,
                    'description' => '',
                    'step_data' => [
                        'step_name' => '',
                        'step_icon' => '',
                    ],
                    'rows' => $rows,
                ],
            ],
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $field_trace_order
     * @return array<string, mixed>
     */
    private function buildFieldIdMap(int $griffinforms_form_id, array $field_trace_order): array
    {
        $export = new FormExport($griffinforms_form_id);
        $form_data = $export->getFormData();
        $imported_fields = $this->getFlatImportedFields(is_array($form_data) ? $form_data : []);
        $field_id_map = [];

        foreach ($field_trace_order as $index => $trace) {
            $imported_field = $imported_fields[$index] ?? null;
            if (!is_array($imported_field)) {
                continue;
            }

            $source_field_id = (string) absint($trace['source_field_id'] ?? 0);
            $mapped_id = absint($imported_field['id'] ?? 0);

            if ($source_field_id === '0' || $mapped_id <= 0) {
                continue;
            }

            $field_id_map[$source_field_id]['primary'] = $mapped_id;
        }

        return $field_id_map;
    }

    /**
     * @param array<string, mixed> $form_data
     * @return array<int, array<string, mixed>>
     */
    private function getFlatImportedFields(array $form_data): array
    {
        $fields = [];

        foreach ((array) ($form_data['pages'] ?? []) as $page) {
            foreach ((array) ($page['rows'] ?? []) as $row) {
                foreach ((array) ($row['columns'] ?? []) as $column) {
                    foreach ((array) ($column['field_data'] ?? []) as $field) {
                        if (is_array($field)) {
                            $fields[] = $field;
                        }
                    }
                }
            }
        }

        return $fields;
    }

    /**
     * @param array<string, mixed> $field_id_map
     */
    private function finalizeImportedForm(int $griffinforms_form_id, int $source_form_id, string $source_title): void
    {
        global $wpdb;

        $table = $this->config->getTable('form');

        $wpdb->update(
            $table,
            [
                'origin' => 'formidable',
                'last_editor' => get_current_user_id(),
                'last_edited' => current_time('mysql', true),
            ],
            ['id' => $griffinforms_form_id],
            ['%s', '%d', '%s'],
            ['%d']
        );
    }

    /**
     * @param array<string, mixed> $form_options
     * @param array<int, array<string, mixed>> $fields
     */
    private function detectConditionalLogic(array $form_options, array $fields): bool
    {
        if (!empty($form_options['conditional_logic']) || !empty($form_options['conditional'])) {
            return true;
        }

        foreach ($fields as $field) {
            $field_options = $this->decodeMaybeSerialized($field['field_options'] ?? '');
            if (is_array($field_options) && (!empty($field_options['conditional_logic']) || !empty($field_options['conditional']))) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param array<string, mixed> $form_data
     * @return array<int, array<string, mixed>>
     */
    private function buildImportedFieldMap(array $form_data): array
    {
        $field_map = [];

        foreach ((array) ($form_data['pages'] ?? []) as $page) {
            $page_id = absint($page['id'] ?? 0);
            foreach ((array) ($page['rows'] ?? []) as $row) {
                foreach ((array) ($row['columns'] ?? []) as $column) {
                    foreach ((array) ($column['field_data'] ?? []) as $field) {
                        $field_id = absint($field['id'] ?? 0);
                        if ($field_id <= 0 || $page_id <= 0) {
                            continue;
                        }

                        $field_map[$field_id] = [
                            'field_id' => $field_id,
                            'page_id' => $page_id,
                            'field_type' => sanitize_key((string) ($field['field_type'] ?? '')),
                        ];
                    }
                }
            }
        }

        return $field_map;
    }

    /**
     * @param array<string, mixed> $field_id_map
     * @param array<int, array<string, mixed>> $imported_field_map
     * @return array<string, array<int|string, mixed>>
     */
    private function buildSubmissionPayload(int $entry_id, array $field_id_map, array $imported_field_map, MigrationResult $result): array
    {
        global $wpdb;

        $rows = $wpdb->get_results(
            $wpdb->prepare("SELECT field_id, meta_value FROM {$wpdb->prefix}frm_item_metas WHERE item_id = %d", $entry_id),
            ARRAY_A
        );

        if (!is_array($rows) || empty($rows)) {
            return [];
        }

        $payload = [];

        foreach ($rows as $row) {
            $source_field_id = (string) absint($row['field_id'] ?? 0);
            if ($source_field_id === '0') {
                continue;
            }

            $mapping = $field_id_map[$source_field_id] ?? null;
            if (!is_array($mapping) || empty($mapping['primary'])) {
                $result->addSkipped($source_field_id, __('No matching GriffinForms field.', 'griffinforms-form-builder'));
                continue;
            }

            $value = $this->decodeMaybeSerialized($row['meta_value'] ?? '');
            if (is_array($value)) {
                $value = implode(', ', array_values(array_filter(array_map(static fn($item): string => sanitize_text_field((string) $item), $value), static fn($item): bool => $item !== '')));
            } else {
                $value = sanitize_text_field((string) $value);
            }

            if ($value === '') {
                continue;
            }

            $mapped_field_id = absint($mapping['primary']);
            if (!isset($imported_field_map[$mapped_field_id])) {
                continue;
            }

            $field_meta = $imported_field_map[$mapped_field_id];
            $page_key = 'page_' . absint($field_meta['page_id'] ?? 0);
            if ($page_key === 'page_0') {
                continue;
            }

            $payload[$page_key][$mapped_field_id] = $value;
        }

        return $payload;
    }

    /**
     * @param array<string, mixed> $form_row
     */
    private function insertSubmission(int $form_id, array $submission_payload, array $form_row, string $created_utc): int
    {
        global $wpdb;

        $table = $this->config->getTable('submission');
        $compliance_mode = !empty($form_row['compliance_mode']);
        $compliance_profile = sanitize_text_field((string) ($form_row['compliance_profile'] ?? ''));
        $compliance_flags = $this->maybeDecode($form_row['compliance_flags'] ?? '');
        $compliance_flags = is_array($compliance_flags) ? $compliance_flags : [];

        $submission_hash = $compliance_mode ? Compliance::hashSubmission($submission_payload) : '';
        $submission_version = $compliance_mode ? 1 : 0;
        $submission_meta = wp_json_encode([
            'events' => [
                [
                    'type' => 'migration_import',
                    'timestamp' => gmdate('c', strtotime($created_utc)),
                    'page_id' => 0,
                    'source' => 'formidable',
                ],
            ],
            'compliance' => [
                'mode' => $compliance_mode ? 1 : 0,
                'profile' => $compliance_mode ? $compliance_profile : '',
                'flags' => $compliance_mode ? $compliance_flags : [],
                'captured_at' => gmdate('c', strtotime($created_utc)),
            ],
        ]);

        $insert_data = [
            'form_id' => $form_id,
            'status' => 1,
            'is_payment_form' => 0,
            'payment_status' => 'paid',
            'public_token' => $this->generatePublicToken(),
            'submission' => maybe_serialize($submission_payload),
            'form_layout_version' => $compliance_mode ? (int) ($form_row['layout_version'] ?? 0) : 0,
            'form_layout_hash' => $compliance_mode ? (string) ($form_row['layout_hash'] ?? '') : '',
            'layout_snapshot_id' => null,
            'submission_version' => $submission_version,
            'submission_hash' => $submission_hash,
            'submission_ip' => '',
            'submission_user_agent' => '',
            'submission_meta' => $submission_meta,
            'source_url' => '',
            'author' => get_current_user_id(),
            'created' => gmdate('Y-m-d H:i:s', strtotime($created_utc)),
        ];

        $insert = $wpdb->insert(
            $table,
            $insert_data,
            ['%d', '%d', '%d', '%s', '%s', '%s', '%d', '%s', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%d', '%s']
        );

        if ($insert === false) {
            return 0;
        }

        $submission_id = (int) $wpdb->insert_id;
        SubmissionHistoryManager::getInstance()->recordRevision(
            $submission_id,
            $form_id,
            $submission_payload,
            'create',
            [
                'actor_user_id' => get_current_user_id(),
                'actor_label' => (string) wp_get_current_user()->display_name,
                'previous_snapshot' => [],
                'update_pointer' => true,
            ]
        );

        return $submission_id;
    }

    private function incrementLifetimeSubmissionCount(int $form_id, int $count): void
    {
        global $wpdb;

        if ($form_id <= 0 || $count <= 0) {
            return;
        }

        $table = $this->config->getTable('form');
        $wpdb->query(
            $wpdb->prepare(
                "UPDATE {$table} SET lifetime_submission_count = lifetime_submission_count + %d WHERE id = %d",
                $count,
                $form_id
            )
        );
    }

    private function generatePublicToken(): string
    {
        try {
            return bin2hex(random_bytes(20));
        } catch (\Throwable $throwable) {
            return wp_generate_password(40, false, false);
        }
    }
}
