<?php

namespace GriffinForms\Includes\Ai\PromptRegistry\Handlers;

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

class GeneratePlaceholderPromptHandler 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_placeholder';
    }

    public function getUnsupportedMessage(): string
    {
        return __('I can only rewrite placeholders for supported text-style fields right now.', '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->resolveSelectedFieldSnapshot($selected_context, $structure);
        if ($field === null) {
            return $this->formatFailure(
                __('I could not find a selected field to update its placeholder.', 'griffinforms-form-builder'),
                'selection_required',
                409,
                ['request_context' => $request_context]
            );
        }

        if (!$this->supportsPlaceholder((string) ($field['field_type'] ?? ''))) {
            return $this->formatFailure(
                __('That field type does not support a visible placeholder.', 'griffinforms-form-builder'),
                'unsupported_prompt_key',
                422,
                ['request_context' => $request_context]
            );
        }

        // Remove intent — clear the placeholder without an AI call
        $normalized_prompt = strtolower(trim($prompt));
        if (preg_match('/\b(remove|clear|delete|erase|reset)\b.*\bplaceholder\b|\bplaceholder\b.*\b(remove|clear|delete|erase|reset)\b/', $normalized_prompt)) {
            $patch = [
                'mode' => 'patch',
                'success_message' => __('Placeholder removed.', 'griffinforms-form-builder'),
                'operations' => [[
                    'op' => 'update_field',
                    'target' => ['scope' => 'field', 'item_id' => absint($field['item_id'] ?? 0)],
                    'changes' => ['placeholder' => ''],
                ]],
            ];
            $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']),
                    '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 remove the placeholder.', 'griffinforms-form-builder')),
                'patch_apply_failed',
                500,
                ['request_context' => $request_context]
            );
        }

        $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)
                : $this->buildReflectionPrompt($prompt, $raw_content, $last_errors, $field, $selected_context, $request_context);

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

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

            if (empty($validation_errors) && is_array($decoded)) {
                $placeholder = $this->normalizeText((string) ($decoded['placeholder'] ?? ''), 180);
                if ($placeholder === '') {
                    $validation_errors[] = __('Placeholder response must include a non-empty placeholder value.', 'griffinforms-form-builder');
                } else {
                    $patch = [
                        'mode' => 'patch',
                        'success_message' => $this->normalizeText((string) ($decoded['success_message'] ?? __('Updated the selected field placeholder.', 'griffinforms-form-builder')), 140),
                        'operations' => [[
                            'op' => 'update_field',
                            'target' => [
                                'scope' => 'field',
                                'item_id' => absint($field['item_id'] ?? 0),
                            ],
                            'changes' => [
                                'placeholder' => $placeholder,
                            ],
                        ]],
                    ];

                    $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 selected field placeholder.', '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 placeholder 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 generate a safe placeholder rewrite. Try giving me an example value.', 'griffinforms-form-builder'),
            'placeholder_validation_failed',
            422,
            ['request_context' => $request_context]
        );
    }

    protected function buildSystemPrompt(): string
    {
        return implode("\n", [
            'You are Griffin Assist in placeholder rewrite mode for GriffinForms.',
            'Return JSON only.',
            'Exact shape: {"placeholder":"string","success_message":"string"}',
            'Use the selected field label, description, current placeholder, field type, and validation rules as context.',
            'If validation rules or allowed values exist, align the placeholder with them.',
            'Return exactly one concrete, non-empty placeholder string.',
            'Do not return required_inputs, choices, explanations, markdown, or code fences.',
            'Do not return an empty placeholder.',
            'Do not ask the user to choose from alternatives.',
        ]);
    }

    protected function buildUserPrompt(string $prompt, array $field, array $selected_context, array $request_context): string
    {
        return implode("\n", [
            'User request:',
            $prompt,
            'Selected 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'] ?? ''),
                'placeholder' => (string) ($field['placeholder'] ?? ''),
                'field_settings' => $this->normalizeContextArray((array) ($field['field_settings'] ?? [])),
                'validations' => (array) ($field['validations'] ?? []),
                'validation_summary' => $this->buildValidationSummary((array) ($field['validations'] ?? []), (string) ($field['field_type'] ?? '')),
            ], 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'] ?? ''),
                'response_family' => (string) ($request_context['response_family'] ?? ''),
                'semantic_intent' => (string) ($request_context['semantic_intent'] ?? ''),
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Write a new placeholder that fits the field label, description, and validation rules.',
        ]);
    }

    protected function buildReflectionPrompt(string $prompt, string $raw_content, array $errors, array $field, array $selected_context, array $request_context): string
    {
        return implode("\n", [
            'Your previous placeholder 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 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'] ?? ''),
                'placeholder' => (string) ($field['placeholder'] ?? ''),
                'field_settings' => $this->normalizeContextArray((array) ($field['field_settings'] ?? [])),
                'validations' => (array) ($field['validations'] ?? []),
                'validation_summary' => $this->buildValidationSummary((array) ($field['validations'] ?? []), (string) ($field['field_type'] ?? '')),
            ], 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 non-empty placeholder string.',
        ]);
    }

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

        $placeholder = $this->normalizeText((string) ($decoded['placeholder'] ?? ''), 180);
        if ($placeholder === '') {
            $errors[] = __('Placeholder response must include a non-empty placeholder value.', 'griffinforms-form-builder');
        }

        return $errors;
    }

    protected function buildValidationSummary(array $validations, string $field_type): array
    {
        $summary = [];

        foreach ($validations as $key => $value) {
            $semantic_key = sanitize_key((string) $key);
            if ($field_type === 'inputurl') {
                if ($semantic_key === 'allowed_tlds') {
                    $summary[] = [
                        'key' => 'allowed_tlds',
                        'meaning' => 'Allowed top-level domains',
                        'values' => array_values((array) $value),
                    ];
                    continue;
                }

                if ($semantic_key === 'blocked_tlds') {
                    $summary[] = [
                        'key' => 'blocked_tlds',
                        'meaning' => 'Blocked top-level domains',
                        'values' => array_values((array) $value),
                    ];
                    continue;
                }
            }

            if ($field_type === 'inputphone' && $semantic_key === 'allowed_country_codes') {
                $summary[] = [
                    'key' => 'allowed_country_codes',
                    'meaning' => 'Allowed phone country codes',
                    'values' => array_values((array) $value),
                ];
                continue;
            }

            $summary[] = [
                'key' => $semantic_key,
                'meaning' => $semantic_key,
                'value' => $value,
            ];
        }

        return $summary;
    }

    protected function normalizeContextArray(array $value): array
    {
        $normalized = [];

        foreach ($value as $key => $item) {
            if ($item === null || $item === '' || $item === []) {
                continue;
            }

            $semantic_key = sanitize_key((string) $key);
            if (is_array($item)) {
                $nested = $this->normalizeContextArray($item);
                if (!empty($nested)) {
                    $normalized[$semantic_key] = $nested;
                }
                continue;
            }

            $normalized[$semantic_key] = is_bool($item) || is_int($item) || is_float($item)
                ? $item
                : sanitize_text_field((string) $item);
        }

        return $normalized;
    }

    protected function resolveSelectedFieldSnapshot(array $selected_context, array $structure): ?array
    {
        $selected_id = absint($selected_context['item_id'] ?? 0);
        if ($selected_id <= 0) {
            return null;
        }

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

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

        return null;
    }

    protected function supportsPlaceholder(string $field_type): bool
    {
        return in_array(sanitize_key($field_type), [
            'inputtext',
            'inputemail',
            'inputphone',
            'inputpassword',
            'inputurl',
            'inputnumber',
            'inputdate',
            'textarea',
            'inputrichtext',
        ], true);
    }

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