<?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 NinjaFormsSource extends AbstractMigrationSource
{
    protected string $slug = 'ninja-forms';
    protected string $label = 'Ninja Forms';

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

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

        $table = $wpdb->prefix . 'nf3_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_table = $wpdb->prefix . 'nf3_forms';
        $fields_table = $wpdb->prefix . 'nf3_fields';
        $forms = $wpdb->get_results(
            "SELECT f.id, f.title, COUNT(fi.id) AS field_count
             FROM {$forms_table} f
             LEFT JOIN {$fields_table} fi ON fi.parent_id = f.id
             GROUP BY f.id, f.title
             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['title'] ?? '')),
                '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 Ninja Forms form ID.', 'griffinforms-form-builder'));
        }

        $form = $wpdb->get_row(
            $wpdb->prepare("SELECT id, title FROM {$wpdb->prefix}nf3_forms WHERE id = %d LIMIT 1", $source_id),
            ARRAY_A
        );

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

        $fields = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, label, `key`, type, parent_id, `order` FROM {$wpdb->prefix}nf3_fields WHERE parent_id = %d ORDER BY `order` ASC, id ASC",
                $source_id
            ),
            ARRAY_A
        );

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

        $field_entries = [];
        $field_trace_order = [];
        $submit_label = '';

        foreach ($fields as $field) {
            $field_id = absint($field['id'] ?? 0);
            if ($field_id <= 0) {
                continue;
            }

            $field_meta = $this->getFieldMeta($field_id);
            if ($submit_label === '') {
                $submit_label = $this->extractSubmitLabel($field, $field_meta);
            }
            $mapped_field = $this->mapField($field, $field_meta, $result);
            if (empty($mapped_field)) {
                continue;
            }

            $field_entries[] = $mapped_field;
            $field_trace_order[] = [
                'source_field_id' => $field_id,
            ];
        }

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

        $form_meta = $this->getFormMeta($source_id);
        $title = sanitize_text_field((string) ($form['title'] ?? ''));
        $description = sanitize_textarea_field((string) ($form_meta['form_title'] ?? ''));
        $payload = $this->buildFormImportPayload($title, $description, $field_entries, $submit_label);
        $importer = new FormImport($payload);
        $griffinforms_form_id = $importer->importForm(false);

        if (!$griffinforms_form_id) {
            return $result->addError(__('GriffinForms could not import the Ninja Forms 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_meta, $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
    {
        return post_type_exists('nf_sub') || $this->hasNfSubmissionPosts();
    }

    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 Ninja Forms submission migration request.', 'griffinforms-form-builder'));
        }

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

        $submission_ids = get_posts([
            'post_type' => 'nf_sub',
            'post_status' => 'any',
            'posts_per_page' => -1,
            'orderby' => 'date',
            'order' => 'ASC',
            'fields' => 'ids',
            'meta_query' => [
                [
                    'key' => '_form_id',
                    'value' => $source_form_id,
                    'compare' => '=',
                ],
            ],
        ]);

        if (!is_array($submission_ids) || empty($submission_ids)) {
            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 ($submission_ids as $submission_id) {
            $submission_id = absint($submission_id);
            if ($submission_id <= 0) {
                continue;
            }

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

            $created_utc = get_post_field('post_modified_gmt', $submission_id);
            if (!$created_utc || $created_utc === '0000-00-00 00:00:00') {
                $created_utc = get_post_field('post_modified', $submission_id);
            }
            if (!$created_utc) {
                $created_utc = current_time('mysql', true);
            }

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

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

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

        return $result;
    }

    /**
     * @return array<string, mixed>
     */
    private function getFieldMeta(int $field_id): array
    {
        global $wpdb;

        $rows = $wpdb->get_results(
            $wpdb->prepare("SELECT `key`, `value`, meta_value FROM {$wpdb->prefix}nf3_field_meta WHERE parent_id = %d", $field_id),
            ARRAY_A
        );

        $meta = [];
        foreach ((array) $rows as $row) {
            $key = sanitize_key((string) ($row['key'] ?? ''));
            if ($key === '') {
                continue;
            }

            $value = $row['meta_value'] ?? $row['value'] ?? '';
            $meta[$key] = $this->decodeMaybeSerialized($value);
        }

        return $meta;
    }

    /**
     * @return array<string, mixed>
     */
    private function getFormMeta(int $form_id): array
    {
        global $wpdb;

        $rows = $wpdb->get_results(
            $wpdb->prepare("SELECT `key`, `value`, meta_value FROM {$wpdb->prefix}nf3_form_meta WHERE parent_id = %d", $form_id),
            ARRAY_A
        );

        $meta = [];
        foreach ((array) $rows as $row) {
            $key = sanitize_key((string) ($row['key'] ?? ''));
            if ($key === '') {
                continue;
            }

            $value = $row['meta_value'] ?? $row['value'] ?? '';
            $meta[$key] = $this->decodeMaybeSerialized($value);
        }

        return $meta;
    }

    /**
     * @param array<string, mixed> $field
     * @param array<string, mixed> $field_meta
     * @return array<string, mixed>
     */
    private function mapField(array $field, array $field_meta, MigrationResult $result): array
    {
        $type = sanitize_key((string) ($field['type'] ?? ''));
        $label = sanitize_text_field((string) ($field['label'] ?? $type));
        $name = sanitize_text_field((string) ($field['key'] ?? $label));
        $is_required = !empty($field_meta['required']) ? 1 : 0;
        $placeholder = sanitize_text_field((string) ($field_meta['placeholder'] ?? ''));
        $description = sanitize_textarea_field((string) ($field_meta['help_text'] ?? ($field_meta['description'] ?? '')));
        $default_value = sanitize_text_field((string) ($field_meta['default'] ?? ($field_meta['value'] ?? '')));

        $type_map = [
            'textbox' => 'inputtext',
            'firstname' => 'inputtext',
            'lastname' => 'inputtext',
            'address' => 'inputaddress',
            'email' => 'inputemail',
            'confirmemail' => 'inputemail',
            'emailconfirmation' => 'inputemail',
            'confirm-email' => 'inputemail',
            'phone' => 'inputphone',
            'textarea' => 'textarea',
            'number' => 'inputnumber',
            'date' => 'inputdate',
            'checkbox' => 'inputcheckbox',
            'terms' => 'termsconditions',
            'hidden' => 'inputtext',
            'password' => 'inputpassword',
            'listselect' => 'selectfield',
            'listradio' => 'inputradio',
            'listcheckbox' => 'inputcheckbox',
            'listmultiselect' => 'selectfield',
            'product' => 'payment',
            'signature' => 'signature',
            'starrating' => 'inputrating',
            'listimage' => 'imageselection',
        ];

        $pro_only_types = ['signature', 'starrating', 'listimage'];

        if (!isset($type_map[$type])) {
            if (in_array($type, ['address2', 'city', 'state', 'zip', 'postal', 'country'], true)) {
                return [];
            }

            $reason_map = [
                'hr' => __('Decorative divider has no GriffinForms equivalent.', 'griffinforms-form-builder'),
                'html' => __('Static HTML block has no GriffinForms equivalent.', 'griffinforms-form-builder'),
                'note' => __('Static HTML block has no GriffinForms equivalent.', 'griffinforms-form-builder'),
                'spam' => __('GriffinForms uses its own spam protection.', 'griffinforms-form-builder'),
                'hcaptcha' => __('GriffinForms uses its own spam protection.', 'griffinforms-form-builder'),
                'recaptcha' => __('GriffinForms uses its own spam protection.', 'griffinforms-form-builder'),
                'recaptcha_v3' => __('GriffinForms uses its own spam protection.', 'griffinforms-form-builder'),
                'turnstile' => __('GriffinForms uses its own spam protection.', 'griffinforms-form-builder'),
                'submit' => __('GriffinForms uses its own submit button.', 'griffinforms-form-builder'),
                'listcountry' => __('Country list field not migrated. Recreate it in GriffinForms as a Select field with your required countries.', 'griffinforms-form-builder'),
                'liststate' => __('State list field not migrated. Recreate it in GriffinForms as a Select field with your required states.', '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'),
            ];

            $result->addSkipped($label, $reason_map[$type] ?? __('Unsupported Ninja Forms field type.', 'griffinforms-form-builder'));
            return [];
        }

        if (in_array($type, $pro_only_types, true) && !$this->config->isRegisteredFieldType($type_map[$type])) {
            $result->addSkipped($label, __('Requires GriffinForms Pro. Install and activate GriffinForms Pro to migrate this field.', 'griffinforms-form-builder'));
            return [];
        }

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

        if ($type === 'checkbox') {
            $checked_value = sanitize_text_field((string) ($field_meta['checked_value'] ?? ($field_meta['value'] ?? '1')));
            if ($checked_value === '') {
                $checked_value = '1';
            }
            $field_options = [[$label => $checked_value]];
        } elseif ($type === 'terms') {
            $field_options = [[$label !== '' ? $label : __('I agree to the terms and conditions', 'griffinforms-form-builder') => 'agree']];
        } elseif ($type === 'product') {
            $price = (float) preg_replace('/[^\d.]/', '', (string) ($field_meta['product_price'] ?? '0'));
            $product_id = 'prod_' . substr(md5((string) ($field['id'] ?? uniqid())), 0, 6);
            $field_options = [[
                'id' => $product_id,
                'name' => $label !== '' ? $label : __('Product', 'griffinforms-form-builder'),
                'price' => $price,
                'description' => $description,
                'image' => '',
                'link' => '',
                'default_qty' => 1,
                'min_qty' => 1,
                'max_qty' => 1,
                'allow_quantity' => 0,
                'featured' => 0,
            ]];
        } elseif ($type === 'listselect') {
            $field_options = $this->buildSelectOptions($field_meta['options'] ?? []);
        } elseif ($type === 'listradio' || $type === 'listcheckbox') {
            $field_options = $this->buildChoiceOptions($field_meta['options'] ?? []);
        } elseif (in_array($type, ['confirmemail', 'emailconfirmation', 'confirm-email'], true)) {
            $result->addSkipped($label, __('Email confirmation behavior is not migrated. Recreate it in GriffinForms using standard email fields and conditional logic or validation rules.', 'griffinforms-form-builder'));
        } elseif ($type === 'listmultiselect') {
            $field_options = $this->buildSelectOptions($field_meta['options'] ?? []);
            $result->addSkipped($label, __('Multi-select UI not preserved. Field migrated as Select.', 'griffinforms-form-builder'));
        } 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'),
            ];
        } elseif ($type === 'number') {
            $range_notes = [];
            foreach (['num_min' => 'Min', 'num_max' => 'Max', 'num_step' => 'Step'] as $meta_key => $note_label) {
                if (isset($field_meta[$meta_key]) && $field_meta[$meta_key] !== '') {
                    $field_settings[$meta_key] = sanitize_text_field((string) $field_meta[$meta_key]);
                    $range_notes[] = $note_label . ': ' . sanitize_text_field((string) $field_meta[$meta_key]);
                }
            }
            if (!empty($range_notes)) {
                $result->addSkipped($label, implode(', ', $range_notes));
            }
        }

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

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

        foreach ($parsed as $option) {
            $label = sanitize_text_field((string) ($option['label'] ?? ''));
            if ($label === '') {
                continue;
            }

            $group[$label] = sanitize_title((string) ($option['value'] ?? $label));
        }

        return ['' => $group];
    }

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

        foreach ($parsed as $option) {
            $label = sanitize_text_field((string) ($option['label'] ?? ''));
            if ($label === '') {
                continue;
            }

            $items[] = [$label => sanitize_title((string) ($option['value'] ?? $label))];
        }

        return $items;
    }

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

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

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

        if (is_serialized((string) $value)) {
            $unserialized = maybe_unserialize($value);
            return is_string($unserialized) && $this->looksLikeJson($unserialized) ? json_decode($unserialized, true) : $unserialized;
        }

        if ($this->looksLikeJson((string) $value)) {
            $decoded = json_decode((string) $value, true);
            return json_last_error() === JSON_ERROR_NONE ? $decoded : $value;
        }

        return $value;
    }

    private function looksLikeJson(string $value): bool
    {
        $value = trim($value);
        return $value !== '' && (($value[0] === '{' && str_ends_with($value, '}')) || ($value[0] === '[' && str_ends_with($value, ']')));
    }

    /**
     * @param array<int, array<string, mixed>> $field_entries
     * @return array<string, mixed>
     */
    private function buildFormImportPayload(string $title, string $description, array $field_entries, string $submit_label = ''): 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' => '',
            'submit_label' => $submit_label !== '' ? $submit_label : __('Submit', 'griffinforms-form-builder'),
            'integration_settings' => [],
            'pages' => [
                [
                    'name' => $title,
                    'heading' => $title,
                    'description' => '',
                    'step_data' => [
                        'step_name' => '',
                        'step_icon' => '',
                    ],
                    'rows' => $rows,
                ],
            ],
        ];
    }

    /**
     * @param array<string, mixed> $field
     * @param array<string, mixed> $field_meta
     */
    private function extractSubmitLabel(array $field, array $field_meta): string
    {
        $type = sanitize_key((string) ($field['type'] ?? ''));
        if ($type !== 'submit') {
            return '';
        }

        $candidates = [
            $field_meta['label'] ?? '',
            $field_meta['value'] ?? '',
            $field['label'] ?? '',
        ];

        foreach ($candidates as $candidate) {
            $label = sanitize_text_field((string) $candidate);
            if ($label !== '') {
                return $label;
            }
        }

        return '';
    }

    /**
     * @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;
    }

    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' => 'ninjaforms',
                '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_meta
     * @param array<int, array<string, mixed>> $fields
     */
    private function detectConditionalLogic(array $form_meta, array $fields): bool
    {
        if (!empty($form_meta['conditional_logic'])) {
            return true;
        }

        foreach ($fields as $field) {
            $field_id = absint($field['id'] ?? 0);
            if ($field_id <= 0) {
                continue;
            }

            $field_meta = $this->getFieldMeta($field_id);
            if (!empty($field_meta['conditional_logic'])) {
                return true;
            }
        }

        return false;
    }

    private function hasNfSubmissionPosts(): bool
    {
        global $wpdb;

        $count = $wpdb->get_var(
            $wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s", 'nf_sub')
        );

        return (int) $count > 0;
    }

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

        foreach ($field_id_map as $source_field_id => $mapping) {
            $source_field_id = absint($source_field_id);
            if ($source_field_id <= 0 || !is_array($mapping)) {
                continue;
            }

            $value = get_post_meta($submission_id, '_field_' . $source_field_id, true);
            if ($value === '' || $value === null || $value === []) {
                continue;
            }

            if (is_array($value)) {
                $value = 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);
            }

            $mapped_field_id = absint($mapping['primary'] ?? 0);
            if ($mapped_field_id <= 0 || !isset($imported_field_map[$mapped_field_id])) {
                $result->addSkipped((string) $source_field_id, __('No matching GriffinForms field.', 'griffinforms-form-builder'));
                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' => 'ninjaforms',
                ],
            ],
            '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);
        }
    }
}
