<?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 WpFormsSource extends AbstractMigrationSource
{
    protected string $slug = 'wpforms';
    protected string $label = 'WPForms Lite';

    public function isInstalled(): bool
    {
        return defined('WPFORMS_VERSION') || post_type_exists('wpforms');
    }

    public function getForms(): array
    {
        if (!$this->isInstalled()) {
            return [];
        }

        $posts = get_posts([
            'post_type' => 'wpforms',
            'post_status' => 'publish',
            'orderby' => 'ID',
            'order' => 'ASC',
            'numberposts' => -1,
            'suppress_filters' => false,
        ]);

        $items = [];

        foreach ($posts as $post) {
            if (!$post instanceof \WP_Post) {
                continue;
            }

            $form_data = $this->decodeWpFormsData($post->post_content);
            $items[] = [
                'source_id' => absint($post->ID),
                'title' => sanitize_text_field((string) $post->post_title),
                'field_count' => count($this->getOrderedSourceFields($form_data)),
            ];
        }

        return $items;
    }

    public function importForm($source_id, array $options = []): MigrationResult
    {
        $result = new MigrationResult();
        $source_id = absint($source_id);

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

        $post = get_post($source_id);
        if (!$post instanceof \WP_Post || $post->post_type !== 'wpforms') {
            return $result->addError(__('WPForms form not found.', 'griffinforms-form-builder'));
        }

        $form_data = $this->decodeWpFormsData($post->post_content);
        $source_fields = $this->getOrderedSourceFields($form_data);

        if (empty($source_fields)) {
            return $result->addError(__('WPForms form has no mappable fields.', 'griffinforms-form-builder'));
        }

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

        foreach ($source_fields as $source_field) {
            $mapped_fields = $this->mapSourceField($source_field, $result);
            if (empty($mapped_fields)) {
                continue;
            }

            $source_field_id = absint($source_field['id'] ?? 0);
            foreach ($mapped_fields as $mapped_field) {
                $field_entries[] = $mapped_field;
                $field_trace_order[] = [
                    'source_field_id' => $source_field_id,
                    'source_field_type' => sanitize_key((string) ($source_field['type'] ?? '')),
                    'target_key' => sanitize_key((string) ($mapped_field['_migration_target_key'] ?? 'primary')),
                ];
                unset($field_entries[array_key_last($field_entries)]['_migration_target_key']);
            }
        }

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

        $title = sanitize_text_field((string) ($form_data['settings']['form_title'] ?? $post->post_title));
        $description = sanitize_textarea_field((string) ($form_data['settings']['form_desc'] ?? ''));
        $payload = $this->buildFormImportPayload($title, $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 WPForms 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, $title);

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

        if ($this->detectConditionalLogic($form_data)) {
            $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 = $this->getEntriesTableName();
        $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 WPForms submission migration request.', 'griffinforms-form-builder'));
        }

        if (!$this->supportsSubmissions()) {
            return $result->addError(__('WPForms 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(__('WPForms field mapping is missing for submission migration.', 'griffinforms-form-builder'));
        }

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                'SELECT entry_id, status, fields, date FROM ' . $this->getEntriesTableName() . ' WHERE form_id = %d ORDER BY date ASC',
                $source_form_id
            ),
            ARRAY_A
        );

        if (!is_array($rows) || empty($rows)) {
            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 ($rows as $row) {
            $entry_id = absint($row['entry_id'] ?? 0);
            $status = sanitize_key((string) ($row['status'] ?? ''));

            if ($status === 'trash') {
                $result->addSkipped((string) $entry_id, __('Trashed WPForms entries are not migrated.', 'griffinforms-form-builder'));
                continue;
            }

            $entry_fields = $this->decodeJsonArray($row['fields'] ?? '');
            $submission_payload = $this->buildSubmissionPayload($entry_fields, $field_id_map, $imported_field_map, $result);
            if (empty($submission_payload)) {
                continue;
            }

            $created_utc = sanitize_text_field((string) ($row['date'] ?? ''));
            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 WPForms 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;
    }

    /**
     * @return array<string, mixed>
     */
    private function decodeWpFormsData($raw_data): array
    {
        if (is_array($raw_data)) {
            return $raw_data;
        }

        if (function_exists('wpforms_decode')) {
            $decoded = wpforms_decode((string) $raw_data);
            return is_array($decoded) ? $decoded : [];
        }

        $decoded = json_decode((string) $raw_data, true);
        return is_array($decoded) ? wp_unslash($decoded) : [];
    }

    /**
     * @param array<string, mixed> $form_data
     * @return array<int, array<string, mixed>>
     */
    private function getOrderedSourceFields(array $form_data): array
    {
        $fields = is_array($form_data['fields'] ?? null) ? $form_data['fields'] : [];
        uasort($fields, static function ($left, $right): int {
            return absint($left['id'] ?? 0) <=> absint($right['id'] ?? 0);
        });

        return array_values(array_filter($fields, static fn($field): bool => is_array($field)));
    }

    /**
     * @param array<string, mixed> $source_field
     * @return array<int, array<string, mixed>>
     */
    private function mapSourceField(array $source_field, MigrationResult $result): array
    {
        $type = sanitize_key((string) ($source_field['type'] ?? ''));
        $label = sanitize_text_field((string) ($source_field['label'] ?? $type));
        $field_name = sanitize_text_field((string) ($source_field['label'] ?? ('Field ' . absint($source_field['id'] ?? 0))));
        $is_required = !empty($source_field['required']) ? 1 : 0;

        switch ($type) {
            case 'text':
                return [$this->buildField('inputtext', $field_name, $label, $is_required, (string) ($source_field['placeholder'] ?? ''), [], [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'textarea':
                return [$this->buildField('textarea', $field_name, $label, $is_required, (string) ($source_field['placeholder'] ?? ''), [], [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'number':
                return [$this->buildNumericField('inputnumber', $field_name, $label, $is_required, $source_field, $result, 'primary')];

            case 'number-slider':
                $result->addSkipped($label, __('Slider UI not preserved. Field migrated as Number.', 'griffinforms-form-builder'));
                return [$this->buildNumericField('inputnumber', $field_name, $label, $is_required, $source_field, $result, 'primary')];

            case 'email':
                return [$this->buildField('inputemail', $field_name, $label, $is_required, (string) ($source_field['placeholder'] ?? ''), [], [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'phone':
                return [$this->buildField('inputphone', $field_name, $label, $is_required, (string) ($source_field['placeholder'] ?? ''), [], [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'address':
                return [$this->buildField('inputaddress', $field_name, $label, $is_required, '', [], $this->buildAddressFieldSettings($label), [], 'primary', (string) ($source_field['description'] ?? ''), '')];

            case 'select':
                return [$this->buildField('selectfield', $field_name, $label, $is_required, (string) ($source_field['placeholder'] ?? ''), $this->buildSelectOptions($source_field), [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'radio':
                return [$this->buildField('inputradio', $field_name, $label, $is_required, '', $this->buildChoiceOptions($source_field), [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'checkbox':
                return [$this->buildField('inputcheckbox', $field_name, $label, $is_required, '', $this->buildChoiceOptions($source_field), [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'gdpr-checkbox':
                $gdpr_label = $this->extractGdprLabel($source_field, $label);
                return [$this->buildField('termsconditions', $field_name, $gdpr_label, $is_required, '', [['I agree' => 'agree']], [], [], 'primary', (string) ($source_field['description'] ?? ''), (string) ($source_field['default_value'] ?? ''))];

            case 'name':
                $result->addSkipped($label, __('Compound name field split into First Name and Last Name.', 'griffinforms-form-builder'));
                return [
                    $this->buildField(
                        'inputtext',
                        $field_name . ' First',
                        $label . ' - ' . __('First Name', 'griffinforms-form-builder'),
                        $is_required,
                        (string) ($source_field['first_placeholder'] ?? ''),
                        [],
                        [],
                        [],
                        'first',
                        (string) ($source_field['description'] ?? ''),
                        (string) ($source_field['first_default_value'] ?? ($source_field['default_value'] ?? ''))
                    ),
                    $this->buildField(
                        'inputtext',
                        $field_name . ' Last',
                        $label . ' - ' . __('Last Name', 'griffinforms-form-builder'),
                        $is_required,
                        (string) ($source_field['last_placeholder'] ?? ''),
                        [],
                        [],
                        [],
                        'last',
                        (string) ($source_field['description'] ?? ''),
                        (string) ($source_field['last_default_value'] ?? ($source_field['default_value'] ?? ''))
                    ),
                ];

            case 'payment-checkbox':
            case 'payment-multiple':
            case 'payment-select':
                $products = [];
                foreach ((array) ($source_field['choices'] ?? []) as $choice) {
                    if (!is_array($choice)) {
                        continue;
                    }
                    $choice_label = sanitize_text_field((string) ($choice['label'] ?? ''));
                    if ($choice_label === '') {
                        continue;
                    }
                    $price = (float) preg_replace('/[^\d.]/', '', (string) ($choice['value'] ?? ($choice['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,
                    ];
                }
                if (empty($products)) {
                    $result->addSkipped($label, __('WPForms payment field had no choices to migrate.', 'griffinforms-form-builder'));
                    return [];
                }
                return [$this->buildField('payment', $field_name, $label, $is_required, '', $products, [], [], 'primary', (string) ($source_field['description'] ?? ''), '')];

            case 'payment-single':
                $price = (float) preg_replace('/[^\d.]/', '', (string) ($source_field['price'] ?? '0'));
                $product_id = 'prod_' . substr(md5((string) ($source_field['id'] ?? uniqid())), 0, 6);
                $product = [
                    'id' => $product_id,
                    'name' => $label !== '' ? $label : __('Product', 'griffinforms-form-builder'),
                    'price' => $price,
                    'description' => sanitize_text_field((string) ($source_field['description'] ?? '')),
                    'image' => '',
                    'link' => '',
                    'default_qty' => 1,
                    'min_qty' => 1,
                    'max_qty' => 1,
                    'allow_quantity' => 0,
                    'featured' => 0,
                ];
                return [$this->buildField('payment', $field_name, $label, $is_required, '', [$product], [], [], 'primary', (string) ($source_field['description'] ?? ''), '')];

            case 'internal-information':
                $result->addSkipped($label, __('Admin-only WPForms informational field has no GriffinForms equivalent.', 'griffinforms-form-builder'));
                return [];
        }

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

    /**
     * @param array<string, mixed> $source_field
     * @return array<string, mixed>
     */
    private function buildNumericField(string $field_type, string $field_name, string $label, int $is_required, array $source_field, MigrationResult $result, string $target_key): array
    {
        $field_settings = [];
        $range_parts = [];

        if (isset($source_field['min']) && $source_field['min'] !== '') {
            $field_settings['min'] = (string) $source_field['min'];
            $range_parts[] = sprintf(__('Min: %s', 'griffinforms-form-builder'), sanitize_text_field((string) $source_field['min']));
        }

        if (isset($source_field['max']) && $source_field['max'] !== '') {
            $field_settings['max'] = (string) $source_field['max'];
            $range_parts[] = sprintf(__('Max: %s', 'griffinforms-form-builder'), sanitize_text_field((string) $source_field['max']));
        }

        if (isset($source_field['step']) && $source_field['step'] !== '') {
            $field_settings['step'] = (string) $source_field['step'];
            $range_parts[] = sprintf(__('Step: %s', 'griffinforms-form-builder'), sanitize_text_field((string) $source_field['step']));
        }

        if (!empty($range_parts)) {
            $result->addSkipped($label, implode(', ', $range_parts));
        }

        return $this->buildField(
            $field_type,
            $field_name,
            $label,
            $is_required,
            (string) ($source_field['placeholder'] ?? ''),
            [],
            $field_settings,
            [],
            $target_key
        );
    }

    /**
     * @param array<string, mixed> $source_field
     * @return array<string, mixed>
     */
    private function buildField(
        string $field_type,
        string $field_name,
        string $heading,
        int $is_required,
        string $placeholder,
        array $field_options,
        array $field_settings,
        array $validations,
        string $target_key,
        string $description = '',
        string $default_value = ''
    ): array {
        return [
            'field_type' => sanitize_key($field_type),
            'name' => sanitize_text_field($field_name),
            'heading' => sanitize_text_field($heading),
            'description' => sanitize_textarea_field($description),
            'is_required' => $is_required,
            'placeholder' => sanitize_text_field($placeholder),
            'default_value' => sanitize_text_field($default_value),
            'field_options' => $field_options,
            'field_settings' => $field_settings,
            'validations' => $validations,
            '_migration_target_key' => sanitize_key($target_key),
        ];
    }

    /**
     * @param array<string, mixed> $source_field
     * @return array<int, array<string, string>>
     */
    private function buildChoiceOptions(array $source_field): array
    {
        $options = [];

        foreach ((array) ($source_field['choices'] ?? []) as $choice) {
            if (!is_array($choice)) {
                continue;
            }

            $label = sanitize_text_field((string) ($choice['label'] ?? ''));
            if ($label === '') {
                continue;
            }

            $value = sanitize_title((string) ($choice['value'] ?? $label));
            $options[] = [$label => $value];
        }

        return $options;
    }

    /**
     * @param array<string, mixed> $source_field
     * @return array<string, array<string, string>>
     */
    private function buildSelectOptions(array $source_field): array
    {
        $options = [];

        foreach ((array) ($source_field['choices'] ?? []) as $choice) {
            if (!is_array($choice)) {
                continue;
            }

            $label = sanitize_text_field((string) ($choice['label'] ?? ''));
            if ($label === '') {
                continue;
            }

            $options[$label] = sanitize_title((string) ($choice['value'] ?? $label));
        }

        return ['' => $options];
    }

    /**
     * @return array<string, mixed>
     */
    private function buildAddressFieldSettings(string $label): array
    {
        return [
            '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'),
            ],
        ];
    }

    /**
     * @param array<string, mixed> $source_field
     */
    private function extractGdprLabel(array $source_field, string $fallback): string
    {
        $choices = (array) ($source_field['choices'] ?? []);
        foreach ($choices as $choice) {
            if (!is_array($choice)) {
                continue;
            }

            $label = sanitize_text_field((string) ($choice['label'] ?? ''));
            if ($label !== '') {
                return $label;
            }
        }

        return $fallback;
    }

    /**
     * @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);
            $target_key = sanitize_key((string) ($trace['target_key'] ?? 'primary'));
            $mapped_id = absint($imported_field['id'] ?? 0);

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

            $field_id_map[$source_field_id][$target_key] = $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' => 'wpforms',
                '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_data
     */
    private function detectConditionalLogic(array $form_data): bool
    {
        foreach ($this->getOrderedSourceFields($form_data) as $field) {
            if (!empty($field['conditionals']) || !empty($field['conditional_logic'])) {
                return true;
            }
        }

        $settings = (array) ($form_data['settings'] ?? []);
        return !empty($settings['conditionals']) || !empty($settings['conditional_logic']);
    }

    private function getEntriesTableName(): string
    {
        global $wpdb;
        return $wpdb->prefix . 'wpforms_entries';
    }

    /**
     * @param mixed $json
     * @return array<string, mixed>
     */
    private function decodeJsonArray($json): array
    {
        if (is_array($json)) {
            return $json;
        }

        $decoded = json_decode((string) $json, true);
        return is_array($decoded) ? $decoded : [];
    }

    /**
     * @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> $entry_fields
     * @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(array $entry_fields, array $field_id_map, array $imported_field_map, MigrationResult $result): array
    {
        $payload = [];

        foreach ($entry_fields as $source_field_id => $entry_field) {
            if (!is_array($entry_field)) {
                continue;
            }

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

            $type = sanitize_key((string) ($entry_field['type'] ?? ''));
            if ($type === 'name') {
                $this->assignSubmissionValue($payload, $mapping['first'] ?? 0, $imported_field_map, sanitize_text_field((string) ($entry_field['first'] ?? '')));
                $this->assignSubmissionValue($payload, $mapping['last'] ?? 0, $imported_field_map, sanitize_text_field((string) ($entry_field['last'] ?? '')));
                continue;
            }

            $value = $entry_field['value'] ?? '';
            if (is_array($value) && $type === 'checkbox') {
                $normalized = array_values(array_filter(array_map(static fn($item): string => sanitize_text_field((string) $item), $value), static fn($item): bool => $item !== ''));
            } else {
                $normalized = $this->normalizeSubmissionValue($type, $value);
            }

            $this->assignSubmissionValue($payload, $mapping['primary'] ?? 0, $imported_field_map, $normalized);
        }

        return $payload;
    }

    /**
     * @param array<string, array<int|string, mixed>> $payload
     * @param mixed $value
     */
    private function assignSubmissionValue(array &$payload, int $mapped_field_id, array $imported_field_map, $value): void
    {
        if ($mapped_field_id <= 0 || !isset($imported_field_map[$mapped_field_id])) {
            return;
        }

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

        if ($value === '' || $value === []) {
            return;
        }

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

    /**
     * @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' => 'wpforms',
                ],
            ],
            '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
            )
        );
    }

    /**
     * @param mixed $value
     * @return mixed
     */
    private function normalizeSubmissionValue(string $field_type, $value)
    {
        if (is_array($value)) {
            return array_values(array_filter(array_map(static fn($item): string => sanitize_text_field((string) $item), $value), static fn($item): bool => $item !== ''));
        }

        $value = trim((string) $value);

        if ($field_type === 'termsconditions' || $field_type === 'gdpr-checkbox') {
            return in_array(strtolower($value), ['1', 'yes', 'true', 'accepted'], true) ? '1' : sanitize_text_field($value);
        }

        return sanitize_text_field($value);
    }

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