<?php

namespace GriffinForms\Includes\Ai\PromptRegistry\Handlers;

use GriffinForms\Includes\Ai\PatchPlanApplier;
use GriffinForms\Includes\Ai\PromptRegistry\StructureSnapshotBuilder;

class GenerateSelectFieldOptionsPromptHandler extends BasePromptHandler
{
    protected const MAX_REFLECTION_RETRIES = 1;

    protected PatchPlanApplier $applier;

    public function __construct()
    {
        parent::__construct();
        $this->applier = new PatchPlanApplier();
    }

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

    public function getUnsupportedMessage(): string
    {
        return __('I can only rewrite dropdown options for a selected select field.', '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);
        $field = $this->resolveSelectedSelectFieldSnapshot($selected_context, $structure);
        if ($field === null) {
            return $this->formatFailure(
                __('Please select a dropdown field before asking for option changes.', 'griffinforms-form-builder'),
                'selection_required',
                409,
                ['request_context' => $request_context]
            );
        }

        $suggestion_id = (string) ($config['suggestion_id'] ?? '');
        $normalized_prompt = strtolower(trim($prompt));
        if ($suggestion_id !== 'selectfield.add_option_groups' && preg_match('/\b(option groups?|grouped options?)\b/', $normalized_prompt)) {
            return $this->formatFailure(
                __('Dropdown fields support grouped optgroup-style choices, but the current prompt asks for explicit option groups that could not be resolved safely.', 'griffinforms-form-builder'),
                'unsupported_prompt_key',
                422,
                ['request_context' => $request_context]
            );
        }

        $context_summary = $this->buildContextSummary($field, $structure);
        $last_errors = [];
        $raw_content = '';
        for ($attempt = 0; $attempt <= self::MAX_REFLECTION_RETRIES; $attempt++) {
            $prompt_text = $attempt === 0
                ? $this->buildUserPrompt($prompt, $field, $selected_context, $request_context, $context_summary)
                : $this->buildReflectionPrompt($prompt, $raw_content, $last_errors, $field, $selected_context, $request_context, $context_summary);

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

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

            $validation_errors = [];
            if ($extract_error !== null) {
                $validation_errors[] = $extract_error;
            } elseif (!is_array($decoded)) {
                $validation_errors[] = __('Dropdown options response must decode to an object.', 'griffinforms-form-builder');
            } else {
                $validation_errors = $this->validatePayload($decoded);
            }

            if (empty($validation_errors) && is_array($decoded)) {
                $options = (array) ($decoded['options'] ?? []);
                $normalized_options = $this->normalizeStructuredOptions($options);
                if (empty($normalized_options)) {
                    $validation_errors[] = __('Dropdown options response must include at least one valid option.', 'griffinforms-form-builder');
                } else {
                    $patch = [
                        'mode' => 'patch',
                        'success_message' => $this->normalizeText((string) ($decoded['success_message'] ?? __('Updated the dropdown options.', 'griffinforms-form-builder')), 140),
                        'operations' => [[
                            'op' => 'update_field',
                            'target' => [
                                'scope' => 'field',
                                'item_id' => absint($field['item_id'] ?? 0),
                            ],
                            'changes' => [
                                'options' => $normalized_options,
                                'field_type' => 'selectfield',
                            ],
                        ]],
                    ];

                    $patch = $this->attachPlanContext($patch, $selected_context, $request_context, $structure);
                    $apply_result = $this->applier->apply($form_id, $patch, $prompt);
                    if (!empty($apply_result['success'])) {
                        return [
                            'success' => true,
                            'status' => 'applied',
                            'message' => (string) ($apply_result['message'] ?? $patch['success_message'] ?? __('Updated the dropdown options.', 'griffinforms-form-builder')),
                            'mutated_ids' => (array) ($apply_result['mutated_ids'] ?? []),
                            'prompt_key' => $this->getPromptKey(),
                            'patch_plan' => $patch,
                            'patch_summary' => (array) ($apply_result['patch_summary'] ?? []),
                        ];
                    }

                    return $this->formatFailure(
                        (string) ($apply_result['message'] ?? __('Griffin Assist could not safely apply the dropdown options update.', 'griffinforms-form-builder')),
                        'bulk_text_apply_failed',
                        500,
                        ['request_context' => $request_context]
                    );
                }
            }

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

        return $this->formatFailure(
            __('I could not rewrite the dropdown options safely. Try giving me clearer option labels.', 'griffinforms-form-builder'),
            'dropdown_options_validation_failed',
            422,
            ['request_context' => $request_context]
        );
    }

    protected function buildSystemPrompt(string $suggestion_id = ''): string
    {
        $base = [
            'You are Griffin Assist updating choices for a single selectfield in GriffinForms.',
            'Return JSON only.',
            'Exact shape: {"options":{ "Group Name": {"Choice label":"choice_value"} },"success_message":"string"}',
            'If there is no optgroup, use an empty string as the group key.',
            'If there are multiple logical groups, use descriptive group names as top-level keys.',
            'Examples:',
            '{"options":{"":{"General Inquiry":"general_inquiry","Feedback":"feedback","Support Request":"support_request"}},"success_message":"Updated the dropdown options."}',
            '{"options":{"Inquiry Topics":{"General Feedback":"general_feedback","Product Inquiry":"product_inquiry","Service Request":"service_request","Website Issue":"website_issue"}},"success_message":"Grouped the dropdown options."}',
            'Keep values machine-friendly (snake_case, lowercase). Do not change values unless explicitly asked.',
            'Do not return required_inputs, markdown, or code fences.',
        ];

        if ($suggestion_id === 'selectfield.add_option_groups') {
            $base[] = 'The user wants to organize existing options into semantic optgroup groups.';
            $base[] = 'You MUST preserve every existing option — do not add or remove any options.';
            $base[] = 'Only reorganize the options under meaningful group names based on their semantic theme.';
            $base[] = 'Do not change option values. Do not change option labels unless minor capitalization fixes are needed.';
            $base[] = 'If the options naturally belong to one group, use a single named group rather than leaving them ungrouped.';
        } else {
            $base[] = 'Rewrite the dropdown choices using the form heading, parent page heading, field label, field description, and current field options.';
            $base[] = 'If the current options are generic placeholders, generate a brand new choice set that fits the semantic context.';
            $base[] = 'If the current options are already meaningful, improve the wording while preserving the same number of choices.';
        }

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

    protected function buildUserPrompt(string $prompt, array $field, array $selected_context, array $request_context, array $context_summary): string
    {
        return implode("\n", [
            'User request:',
            $prompt,
            'Selected dropdown field context:',
            wp_json_encode([
                'item_id' => absint($field['item_id'] ?? 0),
                'field_type' => (string) ($field['field_type'] ?? ''),
                'label' => (string) ($field['label'] ?? ''),
                'description' => (string) ($field['description'] ?? ''),
                'options' => (array) ($field['options'] ?? []),
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Semantic context:',
            wp_json_encode($context_summary, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Selected builder context:',
            wp_json_encode($selected_context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Routing context:',
            wp_json_encode([
                'surface' => (string) ($request_context['surface'] ?? ''),
                'origin' => (string) ($request_context['origin'] ?? ''),
                'response_mode' => (string) ($request_context['response_mode'] ?? ''),
                'semantic_intent' => (string) ($request_context['semantic_intent'] ?? ''),
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Return a grouped associative dropdown choice map.',
            'Example grouped map:',
            '{"Inquiry Topics":{"General Feedback":"general_feedback","Product Inquiry":"product_inquiry","Service Request":"service_request","Website Issue":"website_issue"}}',
            'If the current options are generic placeholders, generate a brand new option set that fits the semantic context.',
            'If the current options are already meaningful, improve the wording while preserving the same number of options.',
        ]);
    }

    protected function buildReflectionPrompt(string $prompt, string $raw_content, array $errors, array $field, array $selected_context, array $request_context, array $context_summary): string
    {
        return implode("\n", [
            'Your previous dropdown options response was invalid.',
            'Original request:',
            $prompt,
            'Validation errors:',
            wp_json_encode(array_values($errors), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Previous raw response:',
            $raw_content,
            'Selected dropdown field context:',
            wp_json_encode([
                'item_id' => absint($field['item_id'] ?? 0),
                'field_type' => (string) ($field['field_type'] ?? ''),
                'label' => (string) ($field['label'] ?? ''),
                'description' => (string) ($field['description'] ?? ''),
                'options' => (array) ($field['options'] ?? []),
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Semantic context:',
            wp_json_encode($context_summary, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Selected builder 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),
            'Return corrected JSON only with a grouped associative choices map.',
            'The grouped associative shape must preserve the optgroup name as the top-level key when one exists.',
        ]);
    }

    protected function buildContextSummary(array $field, array $structure): array
    {
        $field_id = absint($field['item_id'] ?? 0);
        $field_path = $this->resolveSelectedFieldPath($field_id, $structure);
        $page = $field_path !== null ? (array) ($structure['pages'][$field_path['page_index']] ?? []) : [];
        $form = (array) ($structure['form'] ?? []);
        $options = (array) ($field['options'] ?? []);

        return [
            'form_heading' => sanitize_text_field((string) ($form['heading'] ?? '')),
            'parent_page_heading' => sanitize_text_field((string) ($page['heading'] ?? '')),
            'field_label' => sanitize_text_field((string) ($field['label'] ?? '')),
            'field_description' => sanitize_textarea_field((string) ($field['description'] ?? '')),
            'current_options' => $options,
            'option_count' => count($options),
            'is_generic_options' => $this->areGenericDropdownOptions($options),
        ];
    }

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

        if (empty($decoded['options']) || !is_array($decoded['options'])) {
            return [__('Dropdown options response must include an options object.', 'griffinforms-form-builder')];
        }

        return [];
    }

    protected function normalizeStructuredOptions(array $options): array
    {
        if (empty($options)) {
            return [];
        }

        $first = reset($options);
        if (is_array($first) && isset($first['label'], $first['value'])) {
            $group = [];
            foreach ($options as $option) {
                $label = $this->normalizeText((string) ($option['label'] ?? ''), 120);
                $value = sanitize_key((string) ($option['value'] ?? ''));
                if ($label === '' || $value === '') {
                    continue;
                }
                $group[$label] = $value;
            }

            return empty($group) ? [] : ['' => $group];
        }

        $normalized = [];
        foreach ($options as $group_name => $group_options) {
            $group_name = is_string($group_name) ? $this->normalizeText($group_name, 120) : '';
            if (!is_array($group_options) || empty($group_options)) {
                continue;
            }

            $group_map = [];
            foreach ($group_options as $label => $value) {
                if (is_array($value)) {
                    $option_label = $this->normalizeText((string) ($value['label'] ?? ''), 120);
                    $option_value = sanitize_key((string) ($value['value'] ?? ''));
                } else {
                    $option_label = $this->normalizeText((string) $label, 120);
                    $option_value = sanitize_key((string) $value);
                }

                if ($option_label === '' || $option_value === '') {
                    continue;
                }

                $group_map[$option_label] = $option_value;
            }

            if (!empty($group_map)) {
                $normalized[$group_name !== '' ? $group_name : ''] = $group_map;
            }
        }

        return $normalized;
    }

    protected function areGenericDropdownOptions(array $options): bool
    {
        if (empty($options)) {
            return true;
        }

        $generic_patterns = [
            '/^option\s*\d+$/i',
            '/^choice\s*\d+$/i',
            '/^item\s*\d+$/i',
            '/^select\s+an?\s+/i',
            '/^please\s+select/i',
        ];

        foreach ($options as $option) {
            $label = strtolower(trim((string) ($option['label'] ?? '')));
            $value = strtolower(trim((string) ($option['value'] ?? '')));
            if ($label === '' || $value === '') {
                continue;
            }

            foreach ($generic_patterns as $pattern) {
                if (preg_match($pattern, $label) || preg_match($pattern, $value)) {
                    return true;
                }
            }
        }

        return false;
    }

    protected function resolveSelectedSelectFieldSnapshot(array $selected_context, array $structure): ?array
    {
        if (sanitize_key((string) ($selected_context['item_type'] ?? '')) !== 'field') {
            return null;
        }

        $selected_id = absint($selected_context['item_id'] ?? 0);
        if ($selected_id <= 0) {
            return null;
        }

        foreach ((array) ($structure['fields'] ?? []) as $field) {
            if (absint($field['item_id'] ?? 0) === $selected_id && sanitize_key((string) ($field['field_type'] ?? '')) === 'selectfield') {
                return (array) $field;
            }
        }

        return null;
    }

    protected function resolveSelectedFieldPath(int $selected_id, array $structure): ?array
    {
        if ($selected_id <= 0) {
            return null;
        }

        foreach ((array) ($structure['pages'] ?? []) as $page_index => $page) {
            foreach ((array) ($page['rows'] ?? []) as $row_index => $row) {
                foreach ((array) ($row['columns'] ?? []) as $column_index => $column) {
                    foreach ((array) ($column['fields'] ?? []) as $field_index => $field) {
                        if (absint($field['item_id'] ?? 0) === $selected_id) {
                            return [
                                'page_index' => $page_index,
                                'row_index' => $row_index,
                                'column_index' => $column_index,
                                'field_index' => $field_index,
                            ];
                        }
                    }
                }
            }
        }

        return null;
    }

    protected function attachPlanContext(array $plan, array $selected_context, array $request_context, array $structure): array
    {
        $plan['selected_context'] = $selected_context;
        $plan['request_context'] = $request_context;
        $plan['structure'] = $structure;

        return $plan;
    }
}
