<?php
/** Portable, verified form migration between supported form providers. */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

require_once __DIR__ . '/form-migration-compensation.php';
require_once __DIR__ . '/form-migration-contact-form-7-source.php';
require_once __DIR__ . '/form-migration-public-boundaries.php';

/** @return string[] */
function wpagent_form_migration_providers(): array {
	return array( 'wpforms', 'ninja-forms', 'contact-form-7', 'fluent-forms', 'gravity-forms', 'formidable-forms' );
}

/** @return array<string,mixed> */
function wpagent_form_migration_input_schema( bool $confirm ): array {
	$properties = array(
		'source_provider' => array( 'type' => 'string', 'enum' => wpagent_form_migration_providers() ),
		'source_form_id'   => array( 'type' => 'integer', 'minimum' => 1 ),
		'target_provider' => array( 'type' => 'string', 'enum' => wpagent_form_migration_providers() ),
		'title'           => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 255 ),
	);
	$required   = array( 'source_provider', 'source_form_id', 'target_provider' );
	if ( $confirm ) {
		$properties['confirm'] = array( 'type' => 'boolean' );
		$required[]            = 'confirm';
	}
	return array(
		'type'                 => 'object',
		'additionalProperties' => false,
		'properties'           => $properties,
		'required'             => $required,
	);
}

/** @return array<string,mixed> */
function wpagent_form_migration_output_schema(): array {
	return array(
		'type'       => 'object',
		'properties' => array(
			'source_provider'   => array( 'type' => 'string' ),
			'source_form_id'    => array( 'type' => 'integer' ),
			'target_provider'   => array( 'type' => 'string' ),
			'target_form_id'    => array( 'type' => 'integer' ),
			'portable_fields_lossless' => array( 'type' => 'boolean' ),
			'portable_submit_label' => array( 'type' => 'boolean' ),
			'verified'          => array( 'type' => 'boolean' ),
			'target_behavior'   => array( 'type' => 'string', 'enum' => array( 'provider_defaults' ) ),
			'omitted'           => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
			'portable_form'     => array( 'type' => 'object' ),
			'target_form'       => array( 'type' => 'object' ),
			'portable_types'    => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
			'unsupported_fields' => array( 'type' => 'array', 'items' => array( 'type' => 'object' ) ),
		),
	);
}

/** @param mixed $input */
function wpagent_perm_preview_form_migration( $input = null ): bool {
	$input    = is_array( $input ) ? $input : array();
	$provider = sanitize_key( (string) ( $input['source_provider'] ?? '' ) );
	if ( current_user_can( 'manage_options' ) ) {
		return true;
	}
	if ( '' === $provider ) {
		return current_user_can( 'edit_posts' );
	}
	$capabilities = array(
		'wpforms'       => 'wpagently_wpforms_forms_access',
		'ninja-forms'   => 'wpagently_ninjaforms_forms_access',
		'contact-form-7' => 'wpcf7_read_contact_forms',
		'fluent-forms'  => 'fluentform_forms_manager',
		'gravity-forms' => 'gravityforms_edit_forms',
		'formidable-forms' => 'frm_view_forms',
	);
	$capability = $capabilities[ $provider ] ?? '';
	return in_array( $provider, wpagent_form_migration_providers(), true ) && current_user_can( $capability );
}

/** @param mixed $input */
function wpagent_perm_migrate_form( $input = null ): bool {
	$input    = is_array( $input ) ? $input : array();
	$provider = sanitize_key( (string) ( $input['target_provider'] ?? '' ) );
	if ( ! wpagent_perm_preview_form_migration( $input ) ) {
		return false;
	}
	if ( current_user_can( 'manage_options' ) ) {
		return true;
	}
	if ( '' === $provider ) {
		return current_user_can( 'edit_posts' );
	}
	$capabilities = array(
		'wpforms' => array( 'wpagently_wpforms_write_access', 'wpagently_wpforms_forms_access' ),
		'ninja-forms' => array( 'wpagently_ninjaforms_write_access', 'wpagently_ninjaforms_forms_access' ),
		'contact-form-7' => array( 'wpcf7_edit_contact_forms', 'wpcf7_read_contact_forms' ),
		'fluent-forms' => array( 'fluentform_forms_manager' ),
		'gravity-forms' => array( 'gravityforms_create_form', 'gravityforms_edit_forms', 'gravityforms_delete_forms' ),
		'formidable-forms' => array( 'frm_edit_forms', 'frm_view_forms', 'frm_delete_forms' ),
	);
	if ( ! isset( $capabilities[ $provider ] ) ) {
		return false;
	}
	foreach ( $capabilities[ $provider ] as $capability ) {
		if ( ! current_user_can( $capability ) ) {
			return false;
		}
	}
	return true;
}

/** @return array<string,mixed> */
function wpagent_ability_preview_form_migration(): array {
	return array(
		'label'               => __( 'Formularmigration vorprüfen', 'wp-agent-companion' ),
		'description'         => __( 'Liest die portable Feldstruktur eines unterstützten Formularanbieters und meldet nicht übertragbare Felder, ohne etwas zu schreiben.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => wpagent_form_migration_input_schema( false ),
		'output_schema'       => wpagent_form_migration_output_schema(),
		'execute_callback'    => 'wpagent_exec_preview_form_migration',
		'permission_callback' => 'wpagent_perm_preview_form_migration',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => true, 'destructive' => false, 'idempotent' => true ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_ability_migrate_form(): array {
	return array(
		'label'               => __( 'Formular migrieren', 'wp-agent-companion' ),
		'description'         => __( 'Legt nach ausdrücklicher Bestätigung eine verifizierte Strukturkopie beim gewählten Formularanbieter an. Anbieteraktionen und Einträge werden nicht kopiert.', 'wp-agent-companion' ),
		'category'            => 'wp-agent',
		'input_schema'        => wpagent_form_migration_input_schema( true ),
		'output_schema'       => wpagent_form_migration_output_schema(),
		'execute_callback'    => 'wpagent_exec_migrate_form',
		'permission_callback' => 'wpagent_perm_migrate_form',
		'meta'                => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @return mixed|WP_Error */
function wpagent_form_migration_execute_native( string $provider, string $operation, array $input ) {
	$prefixes = array(
		'wpforms'       => 'wpforms/',
		'ninja-forms'   => 'ninjaforms/',
		'contact-form-7' => 'wp-agent/',
		'fluent-forms'  => 'wp-agent/',
		'gravity-forms' => 'wp-agent/',
		'formidable-forms' => 'wp-agent/',
	);
	$own_operations = array(
		'contact-form-7' => array( 'get-form' => 'get-contact-form', 'create-form' => 'create-contact-form', 'delete-form' => 'delete-contact-form' ),
		'fluent-forms' => array( 'get-form' => 'get-fluent-form', 'create-form' => 'create-fluent-form', 'delete-form' => 'delete-fluent-form' ),
		'gravity-forms' => array( 'get-form' => 'get-gravity-form', 'create-form' => 'create-gravity-form', 'delete-form' => 'delete-gravity-form' ),
		'formidable-forms' => array( 'get-form' => 'get-formidable-form', 'create-form' => 'create-formidable-form', 'delete-form' => 'delete-formidable-form' ),
	);
	$suffix = isset( $own_operations[ $provider ] ) ? ( $own_operations[ $provider ][ $operation ] ?? '' ) : $operation;
	$name   = ( $prefixes[ $provider ] ?? '' ) . $suffix;
	$ability = wpagent_companion_get_registered_ability( $name );
	$meta    = $ability && method_exists( $ability, 'get_meta' ) ? $ability->get_meta() : array();
	$public  = isset( $own_operations[ $provider ] )
		? true === ( $meta['show_in_rest'] ?? false )
		: wpagent_companion_ability_is_mcp_public( $ability );
	if ( ! $ability || ! $public ) {
		return new WP_Error( 'wpagent_form_migration_provider_unavailable', __( 'Der benötigte native Formularpfad ist nicht aktiv oder nicht für MCP freigegeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$previous = 'ninja-forms' === $provider ? wpagent_companion_ninjaforms_current_request_ability_names() : array();
	if ( 'ninja-forms' === $provider ) {
		wpagent_companion_ninjaforms_set_request_ability_names( array( $name ) );
	}
	try {
		if ( 'gravity-forms' === $provider && 'create-form' === $operation ) {
			$input = wpagent_form_migration_gravity_public_write_input( $input );
			if ( is_wp_error( $input ) ) {
				return $input;
			}
		}
		if ( 'formidable-forms' === $provider && 'create-form' === $operation ) {
			$input = wpagent_form_migration_formidable_public_write_input( $input );
			if ( is_wp_error( $input ) ) {
				return $input;
			}
		}
		if ( 'fluent-forms' === $provider && 'create-form' === $operation ) {
			return wpagent_exec_create_fluent_form_internal( $input, true );
		}
		$result = $ability->execute( $input );
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		if ( 'formidable-forms' === $provider && 'get-form' === $operation && is_array( $result ) ) {
			return wpagent_form_migration_formidable_public_read_result( $result );
		}
		return $result;
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_form_migration_provider_failed', __( 'Der native Formularanbieter hat die Migration abgebrochen.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	} finally {
		if ( 'ninja-forms' === $provider ) {
			wpagent_companion_ninjaforms_set_request_ability_names( $previous );
		}
	}
}

/** @param mixed $value */
function wpagent_form_migration_text( $value, int $limit ): string {
	$value = is_scalar( $value ) ? sanitize_text_field( (string) $value ) : '';
	return function_exists( 'mb_substr' ) ? mb_substr( $value, 0, $limit ) : substr( $value, 0, $limit );
}

/** @param mixed $value */
function wpagent_form_migration_checked_text( $value, int $limit, bool &$lossy ): string {
	if ( ! is_scalar( $value ) ) {
		$lossy = $lossy || ( null !== $value && array() !== $value );
		return '';
	}
	$raw     = (string) $value;
	$clean   = sanitize_text_field( $raw );
	$length  = function_exists( 'mb_strlen' ) ? mb_strlen( $clean ) : strlen( $clean );
	$lossy   = $lossy || $raw !== $clean || $length > $limit;
	return function_exists( 'mb_substr' ) ? mb_substr( $clean, 0, $limit ) : substr( $clean, 0, $limit );
}

/** @param mixed $choices @return array<int,array{label:string,value:string}> */
function wpagent_form_migration_choices( $choices, bool &$lossy ): array {
	if ( ! is_array( $choices ) ) {
		$lossy = $lossy || ( null !== $choices && '' !== $choices );
		return array();
	}
	if ( count( $choices ) > 200 ) {
		$lossy = true;
	}
	$result = array();
	foreach ( array_slice( array_values( $choices ), 0, 200 ) as $choice ) {
		$choice = is_object( $choice ) ? get_object_vars( $choice ) : ( is_array( $choice ) ? $choice : array() );
		if ( ! $choice ) {
			$lossy = true;
			continue;
		}
		$label = wpagent_form_migration_checked_text( $choice['label'] ?? $choice['value'] ?? '', 500, $lossy );
		$value = wpagent_form_migration_checked_text( $choice['value'] ?? $label, 500, $lossy );
		if ( ! empty( $choice['default'] ) || ! empty( $choice['selected'] ) || ! empty( $choice['calc'] ) || ! empty( $choice['image'] ) ) {
			$lossy = true;
		}
		foreach ( array_diff_key( $choice, array_flip( array( 'label', 'value', 'default', 'selected' ) ) ) as $extra ) {
			if ( ! in_array( $extra, array( null, '', false, 0, '0', array() ), true ) ) {
				$lossy = true;
			}
		}
		if ( '' !== $label ) {
			$result[] = array( 'label' => $label, 'value' => $value );
		} else {
			$lossy = true;
		}
	}
	return $result;
}

/** @return string */
function wpagent_form_migration_portable_type( string $provider, string $type ): string {
	$type = sanitize_key( $type );
	$maps = array(
		'wpforms' => array(
			'text' => 'text', 'email' => 'email', 'textarea' => 'textarea', 'number' => 'number',
			'checkbox' => 'checkbox', 'radio' => 'radio', 'select' => 'select',
		),
		'ninja-forms' => array(
			'textbox' => 'text', 'email' => 'email', 'textarea' => 'textarea', 'number' => 'number',
			'listcheckbox' => 'checkbox', 'listradio' => 'radio', 'listselect' => 'select',
		),
		'contact-form-7' => array(
			'text' => 'text', 'email' => 'email', 'textarea' => 'textarea', 'number' => 'number',
			'checkbox' => 'checkbox', 'radio' => 'radio', 'select' => 'select',
		),
		'fluent-forms' => array(
			'input_text' => 'text', 'input_email' => 'email', 'textarea' => 'textarea', 'input_number' => 'number',
			'input_checkbox' => 'checkbox', 'input_radio' => 'radio', 'select' => 'select',
		),
		'gravity-forms' => array(
			'text' => 'text', 'email' => 'email', 'textarea' => 'textarea', 'number' => 'number',
			'checkbox' => 'checkbox', 'radio' => 'radio', 'select' => 'select',
		),
		'formidable-forms' => array(
			'text' => 'text', 'email' => 'email', 'textarea' => 'textarea', 'number' => 'number',
			'checkbox' => 'checkbox', 'radio' => 'radio', 'select' => 'select',
		),
	);
	return $maps[ $provider ][ $type ] ?? '';
}

/** @return array{label:string,description:string} */
function wpagent_form_migration_cf7_text( string $markup, string $name ): array {
	$result = array( 'label' => '', 'description' => '' );
	$tag_re = '/\[(?:[a-z0-9_-]+)\*?\s+' . preg_quote( $name, '/' ) . '(?:\s[^\]]*)?\]/i';
	if ( ! preg_match( $tag_re, $markup, $tag_match, PREG_OFFSET_CAPTURE ) ) {
		return $result;
	}
	$tag        = (string) $tag_match[0][0];
	$tag_offset = (int) $tag_match[0][1];
	$prefix     = substr( $markup, 0, $tag_offset );
	$open       = strripos( $prefix, '<label' );
	$closed     = strripos( $prefix, '</label>' );
	if ( false !== $open && ( false === $closed || $open > $closed ) ) {
		$open_end = strpos( $markup, '>', $open );
		$close    = stripos( $markup, '</label>', $tag_offset + strlen( $tag ) );
		if ( false !== $open_end && false !== $close ) {
			$before = substr( $markup, $open_end + 1, $tag_offset - $open_end - 1 );
			$after  = substr( $markup, $tag_offset + strlen( $tag ), $close - $tag_offset - strlen( $tag ) );
			$result['label'] = sanitize_text_field( html_entity_decode( wp_strip_all_tags( $before . ' ' . $after ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
		}
	}
	$fieldset_open   = strripos( $prefix, '<fieldset' );
	$fieldset_closed = strripos( $prefix, '</fieldset>' );
	if ( '' === $result['label'] && false !== $fieldset_open && ( false === $fieldset_closed || $fieldset_open > $fieldset_closed ) ) {
		$fieldset_end = stripos( $markup, '</fieldset>', $tag_offset + strlen( $tag ) );
		if ( false !== $fieldset_end ) {
			$fieldset = substr( $markup, $fieldset_open, $fieldset_end + 11 - $fieldset_open );
			if ( preg_match( '/<legend\b[^>]*>(.*?)<\/legend>/is', $fieldset, $legend ) ) {
				$result['label'] = sanitize_text_field( html_entity_decode( wp_strip_all_tags( $legend[1] ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
			}
		}
	}
	$paragraph_open  = strripos( $prefix, '<p' );
	$paragraph_close = strripos( $prefix, '</p>' );
	$paragraph_active = false !== $paragraph_open && ( false === $paragraph_close || $paragraph_open > $paragraph_close );
	$fieldset_active  = false !== $fieldset_open && ( false === $fieldset_closed || $fieldset_open > $fieldset_closed );
	if ( $paragraph_active || $fieldset_active ) {
		$use_fieldset   = $fieldset_active && ( ! $paragraph_active || $fieldset_open > $paragraph_open );
		$container_open = $use_fieldset ? $fieldset_open : $paragraph_open;
		$closing        = $use_fieldset ? '</fieldset>' : '</p>';
		$end = stripos( $markup, $closing, $tag_offset + strlen( $tag ) );
		if ( false !== $end ) {
			$container = substr( $markup, $container_open, $end + strlen( $closing ) - $container_open );
			if ( preg_match( '/<small\b[^>]*class=(?:"[^"]*\bwpagently-field-description\b[^"]*"|\'[^\']*\bwpagently-field-description\b[^\']*\')[^>]*>(.*?)<\/small>/is', $container, $description ) ) {
				$result['description'] = sanitize_text_field( html_entity_decode( wp_strip_all_tags( $description[1] ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
			}
		}
	}
	return $result;
}

/** @param array<string,mixed> $raw @return array<string,mixed> */
function wpagent_form_migration_normalize_cf7_field( array $raw, int $index, string $markup ): array {
	$source_type = sanitize_key( (string) ( $raw['basetype'] ?? $raw['type'] ?? '' ) );
	$type        = wpagent_form_migration_portable_type( 'contact-form-7', $source_type );
	$name        = sanitize_key( (string) ( $raw['name'] ?? '' ) );
	$options     = array_values( array_map( 'strval', is_array( $raw['options'] ?? null ) ? $raw['options'] : array() ) );
	$source_values = is_array( $raw['raw_values'] ?? null ) ? array_values( $raw['raw_values'] ) : array();
	$value_count = count( $source_values );
	$raw_values  = array_values( array_map( 'strval', array_slice( $source_values, 0, 201 ) ) );
	$text        = wpagent_form_migration_cf7_text( $markup, $name );
	$lossy       = '' === $name || '' === $type;
	$reason      = '' === $name ? 'missing_field_name' : ( '' === $type ? 'unsupported_field_type' : '' );
	$placeholder = '';
	$default     = '';
	$choices     = array();
	$allowed_options = array( 'placeholder', 'use_label_element', 'first_as_label' );
	foreach ( $options as $option ) {
		if ( ! in_array( $option, $allowed_options, true ) && ! str_starts_with( $option, 'class:' ) && ! str_starts_with( $option, 'id:' ) ) {
			$lossy = true;
			$reason = '' === $reason ? 'unsupported_options' : $reason;
		}
	}
	if ( in_array( $type, array( 'checkbox', 'radio', 'select' ), true ) ) {
		$value_limit = 'select' === $type && in_array( 'first_as_label', $options, true ) ? 201 : 200;
		if ( $value_count > $value_limit ) {
			$lossy  = true;
			$reason = '' === $reason ? 'too_many_choices' : $reason;
		}
		$raw_values = array_slice( $raw_values, 0, $value_limit );
		if ( 'select' === $type && in_array( 'first_as_label', $options, true ) && $raw_values ) {
			$placeholder = wpagent_form_migration_checked_text( array_shift( $raw_values ), 1000, $lossy );
			$placeholder = explode( '|', $placeholder, 2 )[0];
		}
		foreach ( $raw_values as $choice ) {
			$parts = explode( '|', $choice, 2 );
			$label = wpagent_form_migration_checked_text( $parts[0], 500, $lossy );
			$value = wpagent_form_migration_checked_text( $parts[1] ?? $parts[0], 500, $lossy );
			if ( '' === $label ) {
				$lossy = true;
				continue;
			}
			$choices[] = array( 'label' => $label, 'value' => $value );
		}
	} elseif ( in_array( 'placeholder', $options, true ) ) {
		$placeholder = wpagent_form_migration_checked_text( $raw_values[0] ?? '', 1000, $lossy );
		if ( count( $raw_values ) > 1 ) {
			$lossy = true;
		}
	} elseif ( $raw_values ) {
		$default = wpagent_form_migration_checked_text( $raw_values[0], 5000, $lossy );
		if ( count( $raw_values ) > 1 ) {
			$lossy = true;
		}
	}
	$label = wpagent_form_migration_checked_text( $text['label'], 500, $lossy );
	if ( '' === $label ) {
		$lossy  = true;
		$reason = '' === $reason ? 'missing_structured_label' : $reason;
		$label = wpagent_form_migration_checked_text( ucwords( str_replace( array( '-', '_' ), ' ', $name ) ), 500, $lossy );
	}
	return array(
		'index'       => $index,
		'source_type' => $source_type,
		'type'        => $type,
		'label'       => $label,
		'key'         => $name,
		'description' => wpagent_form_migration_checked_text( $text['description'], 5000, $lossy ),
		'placeholder' => $placeholder,
		'default'     => $default,
		'required'    => 'radio' === $source_type || str_ends_with( (string) ( $raw['type'] ?? '' ), '*' ),
		'choices'     => $choices,
		'normalization_lossy' => $lossy,
		'normalization_reason' => $reason,
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_form_migration_wpforms_raw_form( int $form_id, array $safe_form ) {
	$post = get_post( $form_id );
	if ( ! $post instanceof WP_Post || 'wpforms' !== $post->post_type ) {
		return new WP_Error( 'wpagent_form_migration_bad_provider_response', __( 'Der Formularanbieter lieferte keine lesbare Formularstruktur.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$handler = function_exists( 'wpforms' ) ? wpforms()->obj( 'form' ) : null;
	$raw     = $handler ? $handler->get( $form_id, array( 'content_only' => true ) ) : null;
	if ( ! is_array( $raw ) ) {
		return new WP_Error( 'wpagent_form_migration_bad_provider_response', __( 'Der Formularanbieter lieferte keine lesbare Formularstruktur.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	return array(
		'title'        => (string) ( $safe_form['title'] ?? $post->post_title ),
		'fields'       => is_array( $raw['fields'] ?? null ) ? array_values( $raw['fields'] ) : array(),
		'submit_label' => (string) ( $raw['settings']['submit_text'] ?? $safe_form['settings']['submit_text'] ?? '' ),
	);
}

/** @param array<string,mixed> $raw @return array<string,mixed> */
function wpagent_form_migration_normalize_field( string $provider, array $raw, int $index ): array {
	$settings = is_array( $raw['settings'] ?? null ) ? $raw['settings'] : array();
	$type     = wpagent_form_migration_portable_type( $provider, (string) ( $raw['type'] ?? '' ) );
	$choices  = $raw['choices'] ?? $raw['options'] ?? $settings['options'] ?? array();
	$lossy    = false;
	$field    = array(
		'index'       => $index,
		'source_type' => sanitize_key( (string) ( $raw['type'] ?? '' ) ),
		'type'        => $type,
		'label'       => wpagent_form_migration_checked_text( $raw['label'] ?? $raw['name'] ?? $settings['label'] ?? '', 500, $lossy ),
		'key'         => sanitize_key( (string) ( $raw['key'] ?? $settings['key'] ?? '' ) ),
		'description' => wpagent_form_migration_checked_text( $raw['description'] ?? $raw['help_text'] ?? $settings['help_text'] ?? '', 5000, $lossy ),
		'placeholder' => wpagent_form_migration_checked_text( $raw['placeholder'] ?? $settings['placeholder'] ?? '', 1000, $lossy ),
		'default'     => wpagent_form_migration_checked_text( $raw['default_value'] ?? $raw['default'] ?? $settings['default'] ?? '', 5000, $lossy ),
		'required'    => ! empty( $raw['required'] ?? $settings['required'] ?? false ),
		'choices'     => wpagent_form_migration_choices( $choices, $lossy ),
		'normalization_lossy' => $lossy,
		'normalization_reason' => '',
	);
	if ( '' === $type ) {
		wpagent_form_migration_issue( $field, 'unsupported_field_type' );
	} elseif ( '' === $field['label'] ) {
		wpagent_form_migration_issue( $field, 'missing_field_label' );
	}
	if ( 'wpforms' === $provider ) {
		foreach ( array( 'min', 'max', 'step' ) as $key ) {
			if ( wpagent_form_migration_constraint_meaningful( $raw[ $key ] ?? null ) ) {
				wpagent_form_migration_issue( $field, 'unsupported_field_settings' );
			}
		}
		foreach ( array( 'input_mask', 'choice_limit', 'conditional_logic', 'dynamic_choices', 'show_other_choice', 'randomize', 'enable_image_choices' ) as $key ) {
			if ( wpagent_form_migration_meaningful( $raw[ $key ] ?? null ) ) {
				wpagent_form_migration_issue( $field, 'unsupported_field_settings' );
			}
		}
		if ( wpagent_form_migration_meaningful( $raw['limit_enabled'] ?? null ) ) {
			wpagent_form_migration_issue( $field, 'unsupported_field_settings' );
		}
	} elseif ( 'ninja-forms' === $provider ) {
		foreach ( array( 'num_min', 'num_max', 'num_step' ) as $key ) {
			if ( wpagent_form_migration_constraint_meaningful( $settings[ $key ] ?? null ) ) {
				wpagent_form_migration_issue( $field, 'unsupported_field_settings' );
			}
		}
		foreach ( array( 'input_limit', 'mask', 'custom_mask', 'calc', 'personally_identifiable', 'disable_input', 'rte_enable', 'rte_media', 'rte_mobile', 'multi_select', 'allow_multi', 'conditional_logic' ) as $key ) {
			if ( wpagent_form_migration_meaningful( $settings[ $key ] ?? null ) ) {
				wpagent_form_migration_issue( $field, 'unsupported_field_settings' );
			}
		}
	}
	if ( $field['normalization_lossy'] && '' === $field['normalization_reason'] ) {
		$field['normalization_reason'] = 'unsupported_field_value';
	}
	return $field;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_form_migration_read_portable( string $provider, int $form_id ) {
	if ( 'contact-form-7' === $provider ) {
		$form = wpagent_form_migration_cf7_read_source( $form_id );
		if ( is_wp_error( $form ) ) {
			return $form;
		}
	} else {
		$read_input = array( 'form_id' => $form_id );
		if ( in_array( $provider, array( 'wpforms', 'ninja-forms' ), true ) ) {
			$read_input['include_fields'] = true;
		}
		$result = wpagent_form_migration_execute_native( $provider, 'get-form', $read_input );
		if ( is_wp_error( $result ) ) {
			return $result;
		}
		$form = 'ninja-forms' === $provider && is_array( $result['form'] ?? null ) ? $result['form'] : $result;
	}
	if ( 'wpforms' === $provider && is_array( $form ) ) {
		$form = wpagent_form_migration_wpforms_raw_form( $form_id, $form );
		if ( is_wp_error( $form ) ) {
			return $form;
		}
	}
	if ( ! is_array( $form ) ) {
		return new WP_Error( 'wpagent_form_migration_bad_provider_response', __( 'Der Formularanbieter lieferte keine lesbare Formularstruktur.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( 'fluent-forms' === $provider ) {
		$authorized = wpagent_fluent_forms_get_authorized_form( array( 'form_id' => $form_id ) );
		if ( is_wp_error( $authorized ) ) {
			return $authorized;
		}
		$form        = wpagent_fluent_forms_form_payload( $authorized['form'] );
		$form_fields = is_array( $form['form_fields'] ?? null ) ? $form['form_fields'] : array();
		$raw_fields  = is_array( $form_fields['fields'] ?? null ) ? array_values( $form_fields['fields'] ) : array();
	} else {
		$raw_fields = is_array( $form['fields'] ?? null ) ? array_values( $form['fields'] ) : array();
	}
	if ( count( $raw_fields ) > 200 ) {
		return new WP_Error( 'wpagent_form_migration_too_large', __( 'Das Formular überschreitet die sichere Grenze von 200 Feldern.', 'wp-agent-companion' ), array( 'status' => 413 ) );
	}
	$fields      = array();
	$unsupported = array();
	$text_lossy = false;
	if ( 'fluent-forms' === $provider ) {
		$submit_label = wpagent_form_migration_checked_text( $form['form_fields']['submitButton']['settings']['button_ui']['text'] ?? '', 500, $text_lossy );
	} elseif ( 'gravity-forms' === $provider ) {
		$submit_label = wpagent_form_migration_checked_text( $form['button']['text'] ?? '', 500, $text_lossy );
	} elseif ( 'formidable-forms' === $provider ) {
		$submit_label = '';
	} else {
		$submit_label = wpagent_form_migration_checked_text( $form['submit_label'] ?? '', 500, $text_lossy );
	}
	$submit_label_portable = 'formidable-forms' !== $provider;
	$submit_count = 0;
	$cf7_names    = array();
	foreach ( $raw_fields as $index => $raw ) {
		$raw = is_object( $raw ) ? get_object_vars( $raw ) : ( is_array( $raw ) ? $raw : array() );
		$raw_type = sanitize_key( (string) ( $raw['basetype'] ?? $raw['type'] ?? '' ) );
		if ( 'submit' === $raw_type ) {
			++$submit_count;
			if ( $submit_count > 1 ) {
				$unsupported[] = array(
					'index'       => $index,
					'source_type' => 'submit',
					'label'       => (string) ( $raw['label'] ?? '' ),
					'reason'      => 'multiple_submit_controls',
				);
				continue;
			}
			$settings     = is_array( $raw['settings'] ?? null ) ? $raw['settings'] : array();
			$raw_labels   = is_array( $raw['raw_values'] ?? null ) ? $raw['raw_values'] : array();
			$submit_label = wpagent_form_migration_checked_text( $raw['label'] ?? $settings['label'] ?? $raw_labels[0] ?? '', 500, $text_lossy );
			continue;
		}
		if ( 'contact-form-7' === $provider ) {
			$field = wpagent_form_migration_normalize_cf7_field( $raw, $index, (string) ( $form['form'] ?? '' ) );
		} elseif ( 'fluent-forms' === $provider ) {
			$field = wpagent_form_migration_normalize_fluent_field( $raw, $index );
		} elseif ( 'gravity-forms' === $provider ) {
			$field = wpagent_form_migration_normalize_gravity_field( $raw, $index );
		} elseif ( 'formidable-forms' === $provider ) {
			$field = wpagent_form_migration_normalize_formidable_field( $raw, $index );
		} else {
			$field = wpagent_form_migration_normalize_field( $provider, $raw, $index );
		}
		if ( 'contact-form-7' === $provider ) {
			$field_name = sanitize_key( (string) ( $raw['name'] ?? '' ) );
			if ( '' !== $field_name && isset( $cf7_names[ $field_name ] ) ) {
				$field['normalization_lossy'] = true;
				if ( empty( $field['normalization_reason'] ) ) {
					$field['normalization_reason'] = 'duplicate_field_name';
				}
			}
			if ( '' !== $field_name ) {
				$cf7_names[ $field_name ] = true;
			}
		}
		if ( '' === $field['type'] || $field['normalization_lossy'] ) {
			$issue = array( 'index' => $index, 'source_type' => $field['source_type'], 'label' => $field['label'] );
			if ( '' !== ( $field['normalization_reason'] ?? '' ) ) {
				$issue['reason'] = $field['normalization_reason'];
			}
			$unsupported[] = $issue;
			continue;
		}
		unset( $field['normalization_lossy'], $field['normalization_reason'] );
		$fields[] = $field;
	}
	if ( 'contact-form-7' === $provider && 0 === $submit_count ) {
		$unsupported[] = array(
			'index'       => -1,
			'source_type' => 'submit',
			'label'       => '',
			'reason'      => 'missing_submit_control',
		);
	} elseif ( 'contact-form-7' === $provider && '' === $submit_label ) {
		$unsupported[] = array(
			'index'       => -1,
			'source_type' => 'submit',
			'label'       => '',
			'reason'      => 'missing_submit_label',
		);
	}
	$title_source = 'formidable-forms' === $provider ? ( $form['name'] ?? '' ) : ( $form['title'] ?? '' );
	$title = wpagent_form_migration_checked_text( $title_source, 255, $text_lossy );
	if ( '' === $title ) {
		return new WP_Error( 'wpagent_form_migration_missing_title', __( 'Das Quellformular besitzt keinen übertragbaren Titel.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( $text_lossy ) {
		$unsupported[] = array( 'index' => -1, 'source_type' => 'form-text', 'label' => $title );
	}
	return array(
		'format'             => 'wpagently-portable-form',
		'version'            => 1,
		'title'              => $title,
		'submit_label'       => $submit_label_portable && '' !== $submit_label ? $submit_label : '',
		'submit_label_portable' => $submit_label_portable && '' !== $submit_label,
		'fields'             => $fields,
		'unsupported_fields' => $unsupported,
	);
}

/** @param mixed $input @return array<string,mixed>|WP_Error */
function wpagent_exec_preview_form_migration( $input ) {
	$input  = is_array( $input ) ? $input : array();
	$source = sanitize_key( (string) ( $input['source_provider'] ?? '' ) );
	$target = sanitize_key( (string) ( $input['target_provider'] ?? '' ) );
	if ( $source === $target ) {
		return new WP_Error( 'wpagent_form_migration_same_provider', __( 'Quell- und Zielanbieter müssen verschieden sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$portable = wpagent_form_migration_read_portable( $source, absint( $input['source_form_id'] ?? 0 ) );
	if ( is_wp_error( $portable ) ) {
		return $portable;
	}
	if ( isset( $input['title'] ) ) {
		$target_title = wpagent_form_migration_text( $input['title'], 255 );
		if ( '' === $target_title ) {
			return new WP_Error( 'wpagent_form_migration_invalid_title', __( 'Ein nicht leerer Titel ist erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$portable['title'] = $target_title;
	}
	$target_issues = wpagent_form_migration_target_issues( $target, $portable );
	$unsupported   = array_merge( $portable['unsupported_fields'], $target_issues );
	$submit_label_portable = ! empty( $portable['submit_label_portable'] ) && 'formidable-forms' !== $target;
	$omitted = array( 'markup_layout', 'notifications', 'confirmations', 'conditional_logic', 'provider_settings', 'entries' );
	if ( ! $submit_label_portable ) {
		$omitted[] = 'submit_label';
	}
	return array(
		'source_provider'   => $source,
		'source_form_id'    => absint( $input['source_form_id'] ?? 0 ),
		'target_provider'   => $target,
		'portable_fields_lossless' => empty( $unsupported ),
		'portable_submit_label' => $submit_label_portable,
		'target_behavior'   => 'provider_defaults',
		'omitted'           => $omitted,
		'portable_form'     => $portable,
		'unsupported_fields' => $unsupported,
	);
}

/** @param array<string,mixed> $portable @return array<string,mixed> */
function wpagent_form_migration_target_input( string $provider, array $portable ): array {
	if ( 'contact-form-7' === $provider ) {
		return array( 'title' => $portable['title'], 'form' => wpagent_form_migration_cf7_markup( $portable ) );
	}
	if ( 'fluent-forms' === $provider ) {
		return wpagent_form_migration_fluent_target_input( $portable );
	}
	if ( 'gravity-forms' === $provider ) {
		return wpagent_form_migration_gravity_target_input( $portable );
	}
	if ( 'formidable-forms' === $provider ) {
		return wpagent_form_migration_formidable_target_input( $portable );
	}
	$type_maps = array(
		'wpforms' => array( 'text' => 'text', 'email' => 'email', 'textarea' => 'textarea', 'number' => 'number', 'checkbox' => 'checkbox', 'radio' => 'radio', 'select' => 'select' ),
		'ninja-forms' => array( 'text' => 'textbox', 'email' => 'email', 'textarea' => 'textarea', 'number' => 'number', 'checkbox' => 'listcheckbox', 'radio' => 'listradio', 'select' => 'listselect' ),
	);
	$fields = array();
	foreach ( $portable['fields'] as $field ) {
		$target = array(
			'type'        => $type_maps[ $provider ][ $field['type'] ],
			'label'       => $field['label'],
			'required'    => $field['required'],
			'placeholder' => $field['placeholder'],
			'default_value' => $field['default'],
		);
		$target[ 'ninja-forms' === $provider ? 'help_text' : 'description' ] = $field['description'];
		if ( $field['choices'] ) {
			$target[ 'ninja-forms' === $provider ? 'options' : 'choices' ] = $field['choices'];
		}
		if ( 'ninja-forms' === $provider && '' !== $field['key'] ) {
			$target['key'] = $field['key'];
		}
		$fields[] = $target;
	}
	$input = array( 'title' => $portable['title'], 'fields' => $fields );
	if ( 'wpforms' === $provider && ! empty( $portable['submit_label_portable'] ) ) {
		$input['settings'] = array( 'submit_text' => $portable['submit_label'] );
	} elseif ( 'ninja-forms' === $provider && ! empty( $portable['submit_label_portable'] ) ) {
		$input['submit_label'] = $portable['submit_label'];
	}
	return $input;
}

/** @param mixed $input @return array<string,mixed>|WP_Error */
function wpagent_exec_migrate_form( $input ) {
	$input = is_array( $input ) ? $input : array();
	if ( true !== ( $input['confirm'] ?? false ) ) {
		return new WP_Error( 'wpagent_confirmation_required', __( 'Die Formularmigration benötigt confirm=true.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$preview = wpagent_exec_preview_form_migration( $input );
	if ( is_wp_error( $preview ) ) {
		return $preview;
	}
	if ( ! $preview['portable_fields_lossless'] ) {
		return new WP_Error( 'wpagent_form_migration_lossy', __( 'Die Migration wurde abgebrochen, weil mindestens ein Feld nicht in das portable Schema übertragbar ist.', 'wp-agent-companion' ), array( 'status' => 409, 'unsupported_fields' => $preview['unsupported_fields'] ) );
	}
	$provider = $preview['target_provider'];
	$created  = wpagent_form_migration_execute_native( $provider, 'create-form', wpagent_form_migration_target_input( $provider, $preview['portable_form'] ) );
	if ( is_wp_error( $created ) ) {
		return $created;
	}
	$form_id = in_array( $provider, array( 'contact-form-7', 'fluent-forms', 'gravity-forms', 'formidable-forms' ), true )
		? absint( $created['form']['id'] ?? 0 )
		: absint( $created['form_id'] ?? 0 );
	if ( $form_id <= 0 ) {
		return new WP_Error( 'wpagent_form_migration_create_failed', __( 'Der Zielanbieter lieferte keine gültige Formular-ID.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	// This candidate is captured before portable readback. It is never sent to
	// the client and allows compensation only for the exact object this call made.
	$candidate = wpagent_form_migration_capture_created_target( $provider, $form_id );
	if ( is_wp_error( $candidate ) ) {
		return wpagent_form_migration_recovery_required( $provider, $form_id, 'created_state_unavailable' );
	}
	$actual = wpagent_form_migration_read_portable( $provider, $form_id );
	$expected_fields = wpagent_form_migration_comparable_fields( $preview['portable_form']['fields'] );
	$actual_fields   = is_wp_error( $actual ) ? array() : wpagent_form_migration_comparable_fields( $actual['fields'] );
	$expected_types  = array_values( array_column( $expected_fields, 'type' ) );
	$actual_types    = array_values( array_column( $actual_fields, 'type' ) );
	$verified      = ! is_wp_error( $actual )
		&& $preview['portable_form']['title'] === $actual['title']
		&& ( ! $preview['portable_submit_label'] || $preview['portable_form']['submit_label'] === $actual['submit_label'] )
		&& empty( $actual['unsupported_fields'] )
		&& $expected_fields === $actual_fields;
	if ( ! $verified ) {
		$compensation = wpagent_form_migration_compensate_created_target( $candidate );
		if ( is_wp_error( $compensation ) ) {
			return $compensation;
		}
		return wpagent_form_migration_verification_error(
			true,
			is_wp_error( $actual ) ? $actual->get_error_code() : '',
			$expected_types,
			$actual_types,
			$provider,
			$form_id
		);
	}
	return array(
		'source_provider' => $preview['source_provider'],
		'source_form_id'  => $preview['source_form_id'],
		'target_provider' => $provider,
		'target_form_id'  => $form_id,
		'target_form'     => $actual,
		'portable_types'  => $expected_types,
		'portable_fields_lossless' => true,
		'portable_submit_label' => $preview['portable_submit_label'],
		'target_behavior' => 'provider_defaults',
		'omitted'         => $preview['omitted'],
		'verified'        => true,
		'unsupported_fields' => array(),
	);
}
