<?php

namespace GriffinForms\Includes\Ai\PromptRegistry\Handlers;

use GriffinForms\Includes\Ai\PromptRegistry\StructureSnapshotBuilder;

class AdvisoryResponsePromptHandler extends BasePromptHandler
{
    protected const MAX_REFLECTION_RETRIES = 1;
    protected const ALLOWED_RESPONSE_TYPES = [
        'text',
        'text_with_actions',
        'text_with_choices',
        'clarification_required',
        'patch_with_user_input',
    ];

    public function getPromptKey(): string
    {
        return 'advisory_response';
    }

    public function getUnsupportedMessage(): string
    {
        return __('I can help with advice, suggestions, and copy options here, but I could not shape that into a usable Griffin Assist response.', 'griffinforms-form-builder');
    }

    public function handle(int $form_id, string $prompt, string $provider, array $config): array
    {
        $selected_context = is_array($config['selected_context'] ?? null) ? (array) $config['selected_context'] : [];
        $request_context = is_array($config['request_context'] ?? null) ? (array) $config['request_context'] : [];
        $structure = (new StructureSnapshotBuilder())->buildFromForm($form_id);
        $effective_selected_context = $this->resolveEffectiveSelectedContext($selected_context, $structure, $prompt);
        $selected_summary = $this->buildSelectedSummary($effective_selected_context, $structure);
        $last_errors = [];
        $raw_content = '';


        for ($attempt = 0; $attempt <= self::MAX_REFLECTION_RETRIES; $attempt++) {
            $prompt_text = $attempt === 0
                ? $this->buildUserPrompt($prompt, $effective_selected_context, $request_context, $structure, $selected_summary)
                : $this->buildReflectionPrompt($prompt, $raw_content, $last_errors, $effective_selected_context, $request_context, $selected_summary);


            $response = $this->requestStrict(
                $provider,
                $prompt_text,
                ['system' => $this->buildSystemPrompt()],
                array_merge($config, ['timeout' => $attempt === 0 ? 15 : 10])
            );

            $raw_content = (string) ($response['content'] ?? '');
            [$decoded, $extract_error] = $this->extractor->extract($raw_content);

            $validation_errors = [];
            if ($extract_error !== null) {
                $validation_errors[] = $extract_error;
            } else {
                $validation_errors = $this->validateAdvisoryPayload($decoded);
            }

            if (empty($validation_errors) && is_array($decoded)) {
                $normalized = $this->normalizeAdvisoryPayload($decoded);
                $normalized = $this->attachLocalTextMutations($normalized, $effective_selected_context, $prompt);


                return [
                    'success' => true,
                    'status' => sanitize_key((string) ($normalized['response_type'] ?? 'text')),
                    'message' => (string) ($normalized['message'] ?? ''),
                    'mutated_ids' => [],
                    'prompt_key' => $this->getPromptKey(),
                    'advisory' => array_merge($normalized, [
                        'selected_context' => $this->normalizeSelectedContextPayload($effective_selected_context),
                    ]),
                ];
            }

            $last_errors = $validation_errors;
            $this->logRetryFailure($this->getPromptKey(), $attempt + 1, $validation_errors, $raw_content);
        }


        return $this->formatFailure(
            __('I could not shape that into a guided Griffin Assist reply. Try rephrasing the question more directly or ask for one specific suggestion.', 'griffinforms-form-builder'),
            'advisory_validation_failed'
        );
    }

    protected function buildSystemPrompt(): string
    {
        return implode("\n", [
            'You are Griffin Assist in advisory mode for GriffinForms.',
            'Return JSON only. No markdown fences. No commentary outside JSON.',
            'Supported response types: text, text_with_actions, text_with_choices, clarification_required, patch_with_user_input.',
            'Use text for plain explanation or advice with no follow-up action buttons.',
            'Use text_with_actions when you want to show one or more suggested mutation actions. Each action must include a label and a suggested_prompt.',
            'Use text_with_choices when you want to show 3 or 4 clickable copy choices. Each choice must include a label and a suggested_prompt that can be safely sent as a new mutation request.',
            'Use clarification_required when the user must clarify scope or objective before Griffin Assist can continue.',
            'Use patch_with_user_input when a constrained user choice like Yes/No or 2-4 implementation variants is required before mutation. Represent those choices in required_inputs with label and suggested_prompt.',
            'Every suggested_prompt must be plain text for a fresh follow-up request. Do not emit executable code or JavaScript.',
            'Every action suggested_prompt must be a fully specified standalone Griffin Assist request, not a placeholder.',
            'Target Context Rule: Always include the target type (e.g. "page", "row", "field") and its EXACT heading from the form structure in the suggested_prompt. For pages, always use the page heading (not the internal name/slug). Never use vague references like "the current page", "this page", "the last page", or "the current section". Example: if a page has heading "Your Feedback", write "Add a field to the \'Your Feedback\' page" — not "Add a field to the \'page\' page" or "Add a field to the current page". Use the heading value from the structure summary, not the name value.',
            'Structural suggestion rule: When giving general form advice or responding to open-ended questions about improving or extending the form, proactively include structural improvement actions in text_with_actions. Structural actions include: adding a new page (e.g. "Add a new page called \'Additional Comments\'"), adding a field to a named page (e.g. "Add a checkbox field to the \'User Suggestions\' page"), adding a row to a named page (e.g. "Add a new row with a textarea field to the \'User Suggestions\' page"). For page insertions, do NOT use "after the [form name]" — the form name is not a valid anchor. Just say "Add a new page called \'X\'" with no position qualifier, or use "after the \'[existing page name]\' page" where [existing page name] is an actual page name from the structure. Always use the exact page name from the form structure summary — never "current page" or "this page".',
            'Never use vague follow-up prompts like "Implement it", "Do it", "Use this", "Proceed", "Yes", "No", "Set New Label", or "Cancel".',
            'Exact JSON shape:',
            '{',
            '  "response_type": "text|text_with_actions|text_with_choices|clarification_required|patch_with_user_input",',
            '  "message": "string",',
            '  "actions": [',
            '    {"label":"string","suggested_prompt":"string","semantic_intent":"builder_mutation"}',
            '  ],',
            '  "choices": [',
            '    {"label":"string","suggested_prompt":"string","semantic_intent":"builder_mutation"}',
            '  ],',
            '  "required_inputs": [',
            '    {"label":"string","suggested_prompt":"string","semantic_intent":"builder_mutation"}',
            '  ],',
            '  "suggested_prompts": ["string"]',
            '}',
        ]);
    }

    protected function buildUserPrompt(string $prompt, array $selected_context, array $request_context, array $structure, array $selected_summary): string
    {
        $response_guidance = $this->buildResponseGuidance($prompt, $selected_context);

        $parts = [
            'User request:',
            $prompt,
            'Routing context:',
            wp_json_encode([
                'surface' => (string) ($request_context['surface'] ?? ''),
                'origin' => (string) ($request_context['origin'] ?? ''),
                'response_mode' => (string) ($request_context['response_mode'] ?? ''),
                'response_family' => (string) ($request_context['response_family'] ?? ''),
                'semantic_intent' => (string) ($request_context['semantic_intent'] ?? ''),
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Site context:',
            wp_json_encode((array) ($request_context['site_context'] ?? []), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Selected context:',
            wp_json_encode($selected_context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Selected structure summary:',
            wp_json_encode($selected_summary, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Form structure summary:',
            wp_json_encode($this->condenseStructure($structure), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Response guidance:',
            $response_guidance,
            'Return the most helpful advisory response type for this request.',
        ];

        return implode("\n", $parts);
    }

    protected function buildReflectionPrompt(string $prompt, string $raw_content, array $errors, array $selected_context, array $request_context, array $selected_summary): string
    {
        return implode("\n", [
            'Your previous Griffin Assist advisory response was invalid.',
            'Original user request:',
            $prompt,
            'Validation errors:',
            wp_json_encode(array_values($errors), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Previous raw response:',
            $raw_content,
            'Selected context:',
            wp_json_encode($selected_context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Routing context:',
            wp_json_encode([
                'surface' => (string) ($request_context['surface'] ?? ''),
                'response_mode' => (string) ($request_context['response_mode'] ?? ''),
                'semantic_intent' => (string) ($request_context['semantic_intent'] ?? ''),
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Selected structure summary:',
            wp_json_encode($selected_summary, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Return corrected JSON only.',
        ]);
    }

    protected function validateAdvisoryPayload($decoded): array
    {
        if (!is_array($decoded)) {
            return [__('Advisory payload must decode to an object.', 'griffinforms-form-builder')];
        }

        $errors = [];
        $response_type = sanitize_key((string) ($decoded['response_type'] ?? ''));
        if (!in_array($response_type, self::ALLOWED_RESPONSE_TYPES, true)) {
            $errors[] = __('Unsupported advisory response type.', 'griffinforms-form-builder');
        }

        $message = $this->normalizeText((string) ($decoded['message'] ?? ''), 600);
        if ($message === '') {
            $errors[] = __('Advisory response requires a message.', 'griffinforms-form-builder');
        }

        if ($response_type === 'text_with_actions') {
            $actions = $this->normalizeSuggestionSet((array) ($decoded['actions'] ?? []), 4);
            if (empty($actions)) {
                $errors[] = __('text_with_actions requires at least one valid action.', 'griffinforms-form-builder');
            } elseif ($this->containsGenericFollowUp($actions)) {
                $errors[] = __('text_with_actions must use specific follow-up prompts, not generic action placeholders.', 'griffinforms-form-builder');
            }
        }

        if ($response_type === 'text_with_choices') {
            $choices = $this->normalizeSuggestionSet((array) ($decoded['choices'] ?? []), 4);
            if (count($choices) < 2) {
                $errors[] = __('text_with_choices requires at least two valid choices.', 'griffinforms-form-builder');
            } elseif ($this->containsGenericFollowUp($choices)) {
                $errors[] = __('text_with_choices must use real replacement options, not generic action placeholders.', 'griffinforms-form-builder');
            }
        }

        if ($response_type === 'patch_with_user_input') {
            $required_inputs = $this->normalizeSuggestionSet((array) ($decoded['required_inputs'] ?? []), 4);
            if (empty($required_inputs)) {
                $errors[] = __('patch_with_user_input requires at least one required input choice.', 'griffinforms-form-builder');
            } elseif ($this->containsGenericFollowUp($required_inputs)) {
                $errors[] = __('patch_with_user_input must use specific follow-up prompts, not generic action placeholders.', 'griffinforms-form-builder');
            }
        }

        return $errors;
    }

    protected function normalizeAdvisoryPayload(array $decoded): array
    {
        $response_type = sanitize_key((string) ($decoded['response_type'] ?? 'text'));
        if (!in_array($response_type, self::ALLOWED_RESPONSE_TYPES, true)) {
            $response_type = 'text';
        }

        return [
            'response_type' => $response_type,
            'message' => $this->normalizeText((string) ($decoded['message'] ?? ''), 600),
            'actions' => $this->normalizeSuggestionSet((array) ($decoded['actions'] ?? []), 4),
            'choices' => $this->normalizeSuggestionSet((array) ($decoded['choices'] ?? []), 4),
            'required_inputs' => $this->normalizeSuggestionSet((array) ($decoded['required_inputs'] ?? []), 4),
            'suggested_prompts' => $this->normalizePromptList((array) ($decoded['suggested_prompts'] ?? []), 6),
        ];
    }

    protected function normalizeSuggestionSet(array $items, int $limit): array
    {
        $normalized = [];

        foreach (array_values($items) as $item) {
            if (!is_array($item)) {
                continue;
            }

            $label = $this->normalizeText((string) ($item['label'] ?? ''), 90);
            $suggested_prompt = $this->normalizeText((string) ($item['suggested_prompt'] ?? ''), 220);
            $semantic_intent = sanitize_key((string) ($item['semantic_intent'] ?? 'builder_mutation'));

            if ($label === '' || $suggested_prompt === '') {
                continue;
            }

            if ($this->isGenericFollowUpText(strtolower($label)) || $this->isGenericFollowUpText(strtolower($suggested_prompt))) {
                continue;
            }

            $normalized[] = [
                'label' => $label,
                'suggested_prompt' => $suggested_prompt,
                'semantic_intent' => $semantic_intent !== '' ? $semantic_intent : 'builder_mutation',
            ];

            if (count($normalized) >= $limit) {
                break;
            }
        }

        return $normalized;
    }

    protected function normalizePromptList(array $prompts, int $limit): array
    {
        $normalized = [];

        foreach ($prompts as $prompt) {
            $value = $this->normalizeText((string) $prompt, 180);
            if ($value === '') {
                continue;
            }

            $normalized[] = $value;

            if (count($normalized) >= $limit) {
                break;
            }
        }

        return $normalized;
    }

    protected function normalizeSelectedContextPayload(array $selected_context): array
    {
        return [
            'item_type' => sanitize_key((string) ($selected_context['item_type'] ?? '')),
            'item_id' => absint($selected_context['item_id'] ?? 0),
            'parent_type' => sanitize_key((string) ($selected_context['parent_type'] ?? '')),
            'parent_id' => absint($selected_context['parent_id'] ?? 0),
            'form_id' => absint($selected_context['form_id'] ?? 0),
            'item_label' => sanitize_text_field((string) ($selected_context['item_label'] ?? '')),
        ];
    }

    protected function resolveLocalTextMutationBlueprint(array $selected_context): ?array
    {
        $selected_type = sanitize_key((string) ($selected_context['item_type'] ?? ''));
        if ($selected_type === 'field') {
            return [
                'mutation_type' => 'local_text',
                'target_property' => 'label',
            ];
        }

        return null;
    }

    protected function attachLocalTextMutations(array $normalized, array $selected_context, string $prompt): array
    {
        $blueprint = $this->resolveLocalTextMutationBlueprint($selected_context);
        if (!is_array($blueprint)) {
            return $normalized;
        }

        // Only attach deterministic local text mutations to actual copy choices.
        foreach (['choices'] as $key) {
            if (empty($normalized[$key]) || !is_array($normalized[$key])) {
                continue;
            }

            $normalized[$key] = array_map(function ($item) use ($blueprint, $selected_context) {
                if (!is_array($item) || empty($item['label'])) {
                    return $item;
                }

                $item['local_mutation'] = array_merge(
                    $blueprint,
                    [
                        'value' => sanitize_text_field((string) $item['label']),
                        'selected_context' => $this->normalizeSelectedContextPayload($selected_context),
                    ]
                );

                return $item;
            }, $normalized[$key]);
        }

        return $normalized;
    }

    protected function buildResponseGuidance(string $prompt, array $selected_context): string
    {
        $normalized_prompt = strtolower(trim($prompt));
        $selected_type = sanitize_key((string) ($selected_context['item_type'] ?? ''));

        if (preg_match('/^(what|how|why|suggest|recommend|idea|ideas|brainstorm|improve|extend|expand|enhance|grow|scale)\b/', $normalized_prompt)) {
            return 'Return text_with_actions. Include a mix of content improvements AND structural suggestions where relevant. Structural suggestions include adding a new page or section, adding a new row, or adding a specific field type. Each action must be concrete and self-contained — use specific suggested prompts like "Add a new page called \'Confirmation\' after the current page" or "Add a file upload field to the first page for supporting documents." Do not use vague labels or prompts like "Implement it", "Do it", "Use this", "Yes", or "Cancel".';
        }

        if (
            $selected_type === 'field'
            && preg_match('/\b(rewrite|improve|refine|reword|polish|refresh|better)\b/', $normalized_prompt)
            && preg_match('/\b(label|title|heading)\b/', $normalized_prompt)
        ) {
            return 'Return response_type="text_with_choices" with 3 or 4 actual replacement label suggestions. Do not return yes/no buttons. Do not return generic actions like "Set New Label" or "Cancel". Each choice label must itself be the real candidate field label text.';
        }

        if (
            $selected_type === 'field'
            && preg_match('/\b(rewrite|improve|refine|reword|polish|refresh|better)\b/', $normalized_prompt)
            && preg_match('/\b(description|placeholder)\b/', $normalized_prompt)
        ) {
            return 'Return response_type="text_with_choices" with 3 or 4 actual replacement text suggestions. Do not return yes/no buttons or generic actions.';
        }

        return 'Prefer the most directly useful advisory response type for the request. If it makes sense for the context, include structural suggestions such as adding a new page, adding a new row, or adding a specific field as actions alongside copy or content improvements.';
    }

    protected function containsGenericFollowUp(array $items): bool
    {
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }

            $label = strtolower(trim((string) ($item['label'] ?? '')));
            $suggested_prompt = strtolower(trim((string) ($item['suggested_prompt'] ?? '')));

            if ($this->isGenericFollowUpText($label) || $this->isGenericFollowUpText($suggested_prompt)) {
                return true;
            }
        }

        return false;
    }

    protected function isGenericFollowUpText(string $text): bool
    {
        if ($text === '') {
            return false;
        }

        if (preg_match('/^(implement( it)?|do it|use this|go ahead|proceed|apply|apply it|select this|try this)$/', $text)) {
            return true;
        }

        if (preg_match('/^(yes|no|cancel|continue|set new label)$/', $text)) {
            return true;
        }

        return false;
    }

    protected function condenseStructure(array $structure): array
    {
        $pages = [];

        foreach (array_values((array) ($structure['pages'] ?? [])) as $page) {
            $page_fields = [];
            foreach ((array) ($page['rows'] ?? []) as $row) {
                foreach ((array) ($row['columns'] ?? []) as $column) {
                    foreach ((array) ($column['fields'] ?? []) as $field) {
                        $page_fields[] = [
                            'label' => sanitize_text_field((string) ($field['label'] ?? '')),
                            'field_type' => sanitize_key((string) ($field['field_type'] ?? '')),
                        ];
                    }
                }
            }

            $pages[] = [
                'name' => sanitize_text_field((string) ($page['name'] ?? '')),
                'description' => sanitize_textarea_field((string) ($page['description'] ?? '')),
                'field_count' => count($page_fields),
                'fields' => array_slice($page_fields, 0, 8),
            ];
        }

        return [
            'form' => (array) ($structure['form'] ?? []),
            'page_count' => count($pages),
            'pages' => $pages,
        ];
    }

    protected function buildSelectedSummary(array $selected_context, array $structure): array
    {
        $selected_type = sanitize_key((string) ($selected_context['item_type'] ?? 'form'));
        $selected_id = absint($selected_context['item_id'] ?? 0);

        $summary = [
            'selected_type' => $selected_type,
            'selected_id' => $selected_id,
            'item_label' => sanitize_text_field((string) ($selected_context['item_label'] ?? '')),
        ];

        if ($selected_type === 'form') {
            $summary['form'] = (array) ($structure['form'] ?? []);
            $summary['page_count'] = count((array) ($structure['pages'] ?? []));
            return $summary;
        }

        foreach ((array) ($structure['pages'] ?? []) as $page) {
            if ($selected_type === 'formpage' && absint($page['item_id'] ?? 0) === $selected_id) {
                $summary['page'] = $page;
                return $summary;
            }

            foreach ((array) ($page['rows'] ?? []) as $row) {
                if ($selected_type === 'pagerow' && absint($row['item_id'] ?? 0) === $selected_id) {
                    $summary['page'] = [
                        'name' => $page['name'] ?? '',
                        'description' => $page['description'] ?? '',
                    ];
                    $summary['row'] = $row;
                    return $summary;
                }

                foreach ((array) ($row['columns'] ?? []) as $column) {
                    if ($selected_type === 'rowcolumn' && absint($column['item_id'] ?? 0) === $selected_id) {
                        $summary['page'] = [
                            'name' => $page['name'] ?? '',
                            'description' => $page['description'] ?? '',
                        ];
                        $summary['row'] = [
                            'row_label' => $row['row_label'] ?? '',
                        ];
                        $summary['column'] = $column;
                        return $summary;
                    }

                    foreach ((array) ($column['fields'] ?? []) as $field) {
                        if ($selected_type === 'field' && absint($field['item_id'] ?? 0) === $selected_id) {
                            $summary['page'] = [
                                'name' => $page['name'] ?? '',
                                'description' => $page['description'] ?? '',
                            ];
                            $summary['row'] = [
                                'row_label' => $row['row_label'] ?? '',
                            ];
                            $summary['column'] = [
                                'column_label' => $column['column_label'] ?? '',
                                'width' => $column['width'] ?? 12,
                            ];
                            $summary['field'] = $field;
                            return $summary;
                        }
                    }
                }
            }
        }

        return $summary;
    }

    protected function resolveEffectiveSelectedContext(array $selected_context, array $structure, string $prompt): array
    {
        $normalized = $this->normalizeSelectedContextPayload($selected_context);
        $selected_type = sanitize_key((string) ($normalized['item_type'] ?? ''));

        if (in_array($selected_type, ['field', 'formpage', 'pagerow', 'rowcolumn'], true) && absint($normalized['item_id'] ?? 0) > 0) {
            return $normalized;
        }

        $prompt_normalized = strtolower(trim($prompt));
        if ($selected_type === 'form' && $this->isExplicitFormCopyPrompt($prompt_normalized)) {
            return $normalized;
        }

        $is_field_copy_prompt = $this->isFieldCopyPrompt($prompt_normalized);
        if (!$is_field_copy_prompt) {
            return $normalized;
        }

        $fields = array_values((array) ($structure['fields'] ?? []));
        if (count($fields) === 1) {
            $field = (array) $fields[0];

            return [
                'item_type' => 'field',
                'item_id' => absint($field['item_id'] ?? 0),
                'parent_type' => 'form',
                'parent_id' => absint($normalized['form_id'] ?? 0),
                'form_id' => absint($normalized['form_id'] ?? 0),
                'item_label' => sanitize_text_field((string) ($field['label'] ?? $normalized['item_label'] ?? '')),
            ];
        }

        $matched_field = $this->matchFieldFromPrompt($prompt_normalized, $fields);
        if (!is_array($matched_field)) {
            return $normalized;
        }

        return [
            'item_type' => 'field',
            'item_id' => absint($matched_field['item_id'] ?? 0),
            'parent_type' => 'form',
            'parent_id' => absint($normalized['form_id'] ?? 0),
            'form_id' => absint($normalized['form_id'] ?? 0),
            'item_label' => sanitize_text_field((string) ($matched_field['label'] ?? $normalized['item_label'] ?? '')),
        ];
    }

    protected function isFieldCopyPrompt(string $prompt_normalized): bool
    {
        if (!preg_match('/\b(field|label|labels|placeholder|description|descriptions)\b/', $prompt_normalized)) {
            return false;
        }

        if ($this->isExplicitFormCopyPrompt($prompt_normalized)) {
            return false;
        }

        return !preg_match('/\bpage\b.*\b(heading|headings|title|titles|description|descriptions)\b|\b(heading|headings|title|titles|description|descriptions)\b.*\bpage\b/', $prompt_normalized);
    }

    protected function isExplicitFormCopyPrompt(string $prompt_normalized): bool
    {
        return (bool) preg_match('/\bform\b.*\b(heading|headings|title|titles|description|descriptions)\b|\b(heading|headings|title|titles|description|descriptions)\b.*\bform\b/', $prompt_normalized);
    }

    protected function matchFieldFromPrompt(string $prompt_normalized, array $fields): ?array
    {
        $label_matches = [];
        foreach ($fields as $field) {
            $field = (array) $field;
            $label = $this->normalizeMatchingText((string) ($field['label'] ?? ''));
            if ($label === '') {
                continue;
            }

            if (preg_match('/\b' . preg_quote($label, '/') . '\b/', $prompt_normalized)) {
                $label_matches[] = $field;
            }
        }

        if (count($label_matches) === 1) {
            return (array) $label_matches[0];
        }

        $type_matches = [];
        foreach ($fields as $field) {
            $field = (array) $field;
            $aliases = $this->fieldTypeAliases((string) ($field['field_type'] ?? ''));
            foreach ($aliases as $alias) {
                $alias = $this->normalizeMatchingText($alias);
                if ($alias === '') {
                    continue;
                }

                if (preg_match('/\b' . preg_quote($alias, '/') . '\b/', $prompt_normalized)) {
                    $type_matches[] = $field;
                    break;
                }
            }
        }

        if (count($type_matches) === 1) {
            return (array) $type_matches[0];
        }

        return null;
    }

    protected function normalizeMatchingText(string $value): string
    {
        $value = strtolower(wp_strip_all_tags($value));
        $value = preg_replace('/[^a-z0-9]+/i', ' ', $value);
        $value = trim(preg_replace('/\s+/', ' ', (string) $value));
        return $value;
    }

    protected function fieldTypeAliases(string $field_type): array
    {
        $field_type = sanitize_key($field_type);

        return match ($field_type) {
            'inputemail' => ['email', 'email field', 'email address'],
            'inputphone' => ['phone', 'phone field', 'telephone'],
            'inputurl' => ['url', 'website', 'website url', 'link'],
            'textarea' => ['message', 'text area', 'textarea'],
            'inputnumber' => ['number', 'number field'],
            'inputaddress' => ['address', 'address field'],
            'inputdate' => ['date', 'date field'],
            'signature' => ['signature', 'signature field', 'sign here', 'e-signature'],
            'inputrating' => ['rating', 'rating field', 'star rating', 'score'],
            'inputrichtext' => ['rich text', 'rich text field', 'formatted text', 'wysiwyg', 'editor'],
            'inputradio' => ['radio', 'radio field', 'choice'],
            'inputcheckbox' => ['checkbox', 'checkbox field', 'check box'],
            'selectfield' => ['dropdown', 'select', 'select field'],
            default => [],
        };
    }
}
