<?php

namespace GriffinForms\Includes\Ai\PromptRegistry\Handlers;

use GriffinForms\Admin\Sql\Format;

class GenerateFieldImageOptionsPromptHandler extends BasePromptHandler
{
    protected const MAX_REFLECTION_RETRIES = 1;

    protected const COLUMNS_DESKTOP_MIN = 1;
    protected const COLUMNS_DESKTOP_MAX = 6;
    protected const COLUMNS_MOBILE_MIN  = 1;
    protected const COLUMNS_MOBILE_MAX  = 3;
    protected const ALLOWED_ASPECT_RATIOS = ['1:1', '4:3', '16:9', '3:2', 'free'];

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

    public function getUnsupportedMessage(): string
    {
        return __('I can only generate image option structures for image selection fields.', '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'] : [];

        $field_id   = absint($selected_context['item_id'] ?? 0);
        $field_type = sanitize_key((string) ($selected_context['field_type'] ?? ''));

        if ($field_id <= 0 || !in_array($field_type, ['imageselection', 'payment'], true)) {
            return $this->formatFailure(
                __('Please select an image selection field before asking to generate image options.', 'griffinforms-form-builder'),
                'selection_required',
                409,
                ['request_context' => $request_context]
            );
        }

        $format_sql = Format::getInstance();
        $field = $format_sql->itemValues($field_id, 'field', ['heading', 'field_type', 'field_settings']);

        $current_settings = $this->decodeValue($field->field_settings ?? null);
        if (!is_array($current_settings)) {
            $current_settings = [];
        }

        $current_options = (array) ($current_settings['options'] ?? []);

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

            $response    = $this->requestStrict(
                $provider,
                $prompt_text,
                ['system' => $this->buildSystemPrompt()],
                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[] = __('Image options response must decode to an object.', 'griffinforms-form-builder');
            } else {
                $validation_errors = $this->validatePayload($decoded);
            }

            if (empty($validation_errors) && is_array($decoded)) {
                $new_options  = $this->normalizeOptions((array) ($decoded['options'] ?? []));
                $new_settings = $this->mergeSettings($current_settings, $new_options);
                $format_sql->updateItemValue('field', $field_id, 'field_settings', wp_json_encode($new_settings));
                return [
                    'success'       => true,
                    'status'        => 'applied',
                    'message'       => $this->normalizeText((string) ($decoded['success_message'] ?? __('Generated the image options.', 'griffinforms-form-builder')), 140),
                    'mutated_ids'   => [$field_id],
                    'prompt_key'    => $this->getPromptKey(),
                    'patch_plan'    => [],
                    'patch_summary' => [],
                ];
            }

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

        return $this->formatFailure(
            __('I could not generate image options safely. Try describing what options you want.', 'griffinforms-form-builder'),
            'image_options_validation_failed',
            422,
            ['request_context' => $request_context]
        );
    }

    protected function buildSystemPrompt(): string
    {
        return implode("\n", [
            'You are Griffin Assist generating option structures for an imageselection field in GriffinForms.',
            'Return JSON only.',
            'Exact shape: {"options":[{"image_id":0,"image_url":"","title":"string","description":"string","value":"slug","is_none_option":0}],"success_message":"string"}',
            'Rules:',
            '- image_id must always be 0 (the user will assign images manually).',
            '- image_url must always be empty string "".',
            '- title: concise user-facing label.',
            '- description: optional short supporting text.',
            '- value: sanitize_key-safe slug (lowercase, underscores, no spaces). Must be unique per field.',
            '- is_none_option: 0 for all entries; at most one entry may be 1 only if the user explicitly asks for a "none" option.',
            '- Return 2–8 options unless the prompt specifies a different count.',
            '- Do not return markdown, code fences, or required_inputs.',
        ]);
    }

    protected function buildUserPrompt(string $prompt, $field, string $field_type, array $current_settings, array $current_options, array $selected_context, array $request_context): string
    {
        return implode("\n", [
            'User request:',
            $prompt,
            'Selected field:',
            wp_json_encode([
                'field_id'        => absint($selected_context['item_id'] ?? 0),
                'field_type'      => $field_type,
                'label'           => sanitize_text_field((string) ($field->heading ?? '')),
                'existing_options'=> $current_options,
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Generate the image option structures. Set image_id to 0 and image_url to "" — the user will upload images separately.',
        ]);
    }

    protected function buildReflectionPrompt(string $prompt, string $raw_content, array $errors): string
    {
        return implode("\n", [
            'Your previous image 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,
            'Return corrected JSON only with a valid options array.',
        ]);
    }

    protected function validatePayload(array $decoded): array
    {
        $options = $decoded['options'] ?? null;
        if (!is_array($options) || empty($options)) {
            return [__('options must be a non-empty array.', 'griffinforms-form-builder')];
        }

        $errors = [];
        $values = [];
        $none_count = 0;
        foreach ($options as $i => $option) {
            if (!is_array($option)) {
                $errors[] = sprintf(__('Option entry %d is not an object.', 'griffinforms-form-builder'), $i + 1);
                continue;
            }
            $value = sanitize_key((string) ($option['value'] ?? ''));
            if ($value === '') {
                $errors[] = sprintf(__('Option entry %d is missing a valid value slug.', 'griffinforms-form-builder'), $i + 1);
            } elseif (in_array($value, $values, true)) {
                $errors[] = sprintf(__('Option entry %d has a duplicate value "%s".', 'griffinforms-form-builder'), $i + 1, $value);
            } else {
                $values[] = $value;
            }
            if (!empty($option['is_none_option'])) {
                $none_count++;
            }
        }

        if ($none_count > 1) {
            $errors[] = __('Only one option may have is_none_option set to 1.', 'griffinforms-form-builder');
        }

        return $errors;
    }

    protected function normalizeOptions(array $options): array
    {
        $normalized = [];
        foreach ($options as $option) {
            if (!is_array($option)) {
                continue;
            }
            $value = sanitize_key((string) ($option['value'] ?? ''));
            if ($value === '') {
                continue;
            }
            $normalized[] = [
                'image_id'       => 0,
                'image_url'      => '',
                'title'          => $this->normalizeText((string) ($option['title'] ?? ''), 255),
                'description'    => $this->normalizeText((string) ($option['description'] ?? ''), 500),
                'value'          => $value,
                'is_none_option' => (int) (bool) ($option['is_none_option'] ?? 0),
            ];
        }
        return $normalized;
    }

    protected function mergeSettings(array $current, array $new_options): array
    {
        $current['options'] = $new_options;

        // Ensure display settings have valid defaults when absent
        if (!isset($current['columns_desktop'])) {
            $current['columns_desktop'] = 3;
        }
        if (!isset($current['columns_mobile'])) {
            $current['columns_mobile'] = 2;
        }
        if (!isset($current['show_title'])) {
            $current['show_title'] = 1;
        }
        if (!isset($current['show_description'])) {
            $current['show_description'] = 1;
        }
        if (!isset($current['aspect_ratio'])) {
            $current['aspect_ratio'] = '1:1';
        }
        if (!isset($current['randomise'])) {
            $current['randomise'] = 0;
        }
        if (!isset($current['default_selected'])) {
            $current['default_selected'] = [];
        }

        return $current;
    }

    protected function decodeValue($value)
    {
        if (is_array($value)) {
            return $value;
        }

        if (!is_string($value) || $value === '') {
            return $value;
        }

        $maybe_unserialized = maybe_unserialize($value);
        if ($maybe_unserialized !== $value || $value === 'b:0;') {
            return $maybe_unserialized;
        }

        $decoded_json = json_decode($value, true);
        if (json_last_error() === JSON_ERROR_NONE) {
            return $decoded_json;
        }

        return $value;
    }
}
