<?php

namespace GriffinForms\Includes\Ai\PromptRegistry\Handlers;

use GriffinForms\Admin\Sql\Format;

class GeneratePaymentProductsPromptHandler extends BasePromptHandler
{
    protected const MAX_REFLECTION_RETRIES = 1;

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

    public function getUnsupportedMessage(): string
    {
        return __('I can only manage products for a selected payment 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'] : [];

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

        if ($field_id <= 0 || $field_type !== 'payment') {
            return $this->formatFailure(
                __('Please select a payment field before asking to manage products.', '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_options', 'field_settings']);

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

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

        $last_errors = [];
        $raw_content = '';
        for ($attempt = 0; $attempt <= self::MAX_REFLECTION_RETRIES; $attempt++) {
            $prompt_text = $attempt === 0
                ? $this->buildUserPrompt($prompt, $field, $current_products, $current_settings, $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[] = __('Payment products response must decode to an object.', 'griffinforms-form-builder');
            } else {
                $validation_errors = $this->validatePayload($decoded);
            }

            if (empty($validation_errors) && is_array($decoded)) {
                $new_products = $this->normalizeProducts((array) ($decoded['products'] ?? []));
                if (empty($new_products)) {
                    $validation_errors[] = __('Payment products response must include at least one valid product.', 'griffinforms-form-builder');
                } else {
                    $format_sql->updateItemValue('field', $field_id, 'field_options', maybe_serialize($new_products));
                    return [
                        'success'       => true,
                        'status'        => 'applied',
                        'message'       => $this->normalizeText((string) ($decoded['success_message'] ?? __('Updated the payment products.', '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 update the payment products safely. Try being more specific about which products to change.', 'griffinforms-form-builder'),
            'payment_products_validation_failed',
            422,
            ['request_context' => $request_context]
        );
    }

    protected function buildSystemPrompt(): string
    {
        return implode("\n", [
            'You are Griffin Assist managing payment products for a payment field in GriffinForms.',
            'Return JSON only.',
            'Exact shape: {"products":[{"id":"prod_xxx","name":"string","price":float,"description":"string","image":"","link":"","default_qty":int,"min_qty":int,"max_qty":int,"allow_quantity":0,"featured":0}],"success_message":"string"}',
            'Rules:',
            '- Return the full products array — partial arrays will lose existing products.',
            '- "name" is the display name (NOT "label").',
            '- "price" is a float (NOT "amount").',
            '- "id" must be unique: "prod_" + 6 lowercase alphanumeric characters. Preserve existing IDs.',
            '- "allow_quantity" and "featured" are integers 0 or 1 (not booleans).',
            '- "image" is a URL string or empty string (not an attachment ID).',
            '- Never modify gateway_options.',
            '- Do not return markdown, code fences, or required_inputs.',
        ]);
    }

    protected function buildUserPrompt(string $prompt, $field, array $current_products, array $current_settings, array $selected_context, array $request_context): string
    {
        // Strip gateway_options from the settings context sent to the model
        $safe_settings = $current_settings;
        unset($safe_settings['gateway_options']);

        return implode("\n", [
            'User request:',
            $prompt,
            'Selected payment field:',
            wp_json_encode([
                'field_id'         => absint($selected_context['item_id'] ?? 0),
                'label'            => sanitize_text_field((string) ($field->heading ?? '')),
                'current_products' => $current_products,
                'field_settings'   => $safe_settings,
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Apply the requested change. Return the full updated products array.',
        ]);
    }

    protected function buildReflectionPrompt(string $prompt, string $raw_content, array $errors): string
    {
        return implode("\n", [
            'Your previous payment products 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 products array.',
        ]);
    }

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

        $errors = [];
        foreach ($products as $i => $product) {
            if (!is_array($product)) {
                $errors[] = sprintf(__('Product entry %d is not an object.', 'griffinforms-form-builder'), $i + 1);
                continue;
            }
            if (empty($product['name']) || !is_string($product['name'])) {
                $errors[] = sprintf(__('Product entry %d is missing a valid name.', 'griffinforms-form-builder'), $i + 1);
            }
            if (!isset($product['price']) || !is_numeric($product['price'])) {
                $errors[] = sprintf(__('Product entry %d is missing a valid price.', 'griffinforms-form-builder'), $i + 1);
            }
        }

        return $errors;
    }

    protected function normalizeProducts(array $products): array
    {
        $normalized = [];
        foreach ($products as $product) {
            if (!is_array($product)) {
                continue;
            }
            $id = sanitize_key((string) ($product['id'] ?? ''));
            if ($id === '') {
                $id = 'prod_' . substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 6);
            }
            $normalized[] = [
                'id'             => $id,
                'name'           => $this->normalizeText((string) ($product['name'] ?? ''), 255),
                'price'          => round((float) ($product['price'] ?? 0), 2),
                'description'    => $this->normalizeText((string) ($product['description'] ?? ''), 1000),
                'image'          => esc_url_raw((string) ($product['image'] ?? '')),
                'link'           => esc_url_raw((string) ($product['link'] ?? '')),
                'default_qty'    => max(1, absint($product['default_qty'] ?? 1)),
                'min_qty'        => max(1, absint($product['min_qty'] ?? 1)),
                'max_qty'        => max(1, absint($product['max_qty'] ?? 1)),
                'allow_quantity' => (int) (bool) ($product['allow_quantity'] ?? 0),
                'featured'       => (int) (bool) ($product['featured'] ?? 0),
            ];
        }
        return $normalized;
    }

    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;
    }
}
