<?php

namespace GriffinForms\Includes\Ai\PromptRegistry\Handlers;

use GriffinForms\Admin\Sql\Format;

class GenerateStepLabelsPromptHandler extends BasePromptHandler
{
    protected const MAX_REFLECTION_RETRIES = 1;

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

    public function getUnsupportedMessage(): string
    {
        return __('I can only rewrite step labels on multi-step forms.', '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'] : [];

        $format_sql = Format::getInstance();
        $form = $format_sql->itemValues($form_id, 'form', ['name', 'heading', 'formpages', 'progress_bar']);

        $page_ids = $this->decodeValue($form->formpages ?? []);
        $page_ids = is_array($page_ids) ? array_values(array_map('absint', $page_ids)) : [];

        if (count($page_ids) < 2) {
            return $this->formatFailure(
                __('Step labels are only available on multi-step forms. This form has only one page.', 'griffinforms-form-builder'),
                'selection_required',
                409,
                ['request_context' => $request_context]
            );
        }

        $pages = [];
        foreach ($page_ids as $page_id) {
            $raw_step_data = $format_sql->getItemValue($page_id, 'step_data', 'formpage');
            $step_data = maybe_unserialize($raw_step_data);
            if (!is_array($step_data)) {
                $step_data = [];
            }

            $pages[] = [
                'page_id'   => $page_id,
                'step_name' => sanitize_text_field((string) ($step_data['step_name'] ?? '')),
                'step_icon' => sanitize_text_field((string) ($step_data['step_icon'] ?? '')),
            ];
        }

        $progress_bar = $this->decodeValue($form->progress_bar ?? null);
        $progress_bar_enabled = is_array($progress_bar) && !empty($progress_bar['toggle']);
        $progress_bar_style   = is_array($progress_bar) ? sanitize_key((string) ($progress_bar['style'] ?? '')) : '';

        if (!$progress_bar_enabled) {
            return $this->formatFailure(
                __('Step labels are only visible when the Steps display is enabled. Turn on Steps in the form settings and try again.', 'griffinforms-form-builder'),
                'selection_required',
                409,
                ['request_context' => $request_context]
            );
        }

        $last_errors = [];
        $raw_content = '';
        for ($attempt = 0; $attempt <= self::MAX_REFLECTION_RETRIES; $attempt++) {
            $prompt_text = $attempt === 0
                ? $this->buildUserPrompt($prompt, $form, $pages, $progress_bar_enabled, $progress_bar_style, $request_context)
                : $this->buildReflectionPrompt($prompt, $raw_content, $last_errors, $pages);

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

            if (empty($validation_errors) && is_array($decoded)) {
                $step_labels = (array) ($decoded['step_labels'] ?? []);
                $write_result = $this->applyStepLabels($form_id, $pages, $step_labels, $format_sql);
                if ($write_result['success']) {
                    return [
                        'success'     => true,
                        'status'      => 'applied',
                        'message'     => $this->normalizeText((string) ($decoded['success_message'] ?? __('Updated the step labels.', 'griffinforms-form-builder')), 140),
                        'mutated_ids' => $write_result['mutated_ids'],
                        'prompt_key'  => $this->getPromptKey(),
                        'patch_plan'  => [],
                        'patch_summary' => [],
                    ];
                }

                return $this->formatFailure(
                    __('Griffin Assist could not safely write the updated step labels.', 'griffinforms-form-builder'),
                    'step_labels_write_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 step labels safely. Try giving me clearer context about the form steps.', 'griffinforms-form-builder'),
            'step_labels_validation_failed',
            422,
            ['request_context' => $request_context]
        );
    }

    protected function buildSystemPrompt(): string
    {
        return implode("\n", [
            'You are Griffin Assist rewriting step labels for a multi-step form in GriffinForms.',
            'Return JSON only.',
            'Exact shape: {"step_labels":[{"page_id":int,"step_name":"string"}],"success_message":"string"}',
            'Rules:',
            '- Return one entry per page in the same order as the input.',
            '- step_name must be a non-empty string, concise and user-friendly (2–4 words preferred).',
            '- Do not return step_icon — it is preserved automatically.',
            '- Do not return markdown, code fences, or required_inputs.',
            '- success_message must describe the AI operation outcome (e.g. "Updated the step labels." or "Polished 4 step labels."). Never write user-facing form copy, submission confirmations, or anything that could be mistaken for a form success message.',
        ]);
    }

    protected function buildUserPrompt(string $prompt, $form, array $pages, bool $progress_bar_enabled, string $progress_bar_style, array $request_context): string
    {
        $step_context = array_map(fn($p) => ['page_id' => $p['page_id'], 'step_name' => $p['step_name']], $pages);

        $lines = [
            'User request:',
            $prompt,
            'Form context:',
            wp_json_encode([
                'form_heading'          => sanitize_text_field((string) ($form->heading ?? '')),
                'progress_bar_enabled'  => $progress_bar_enabled,
                'progress_bar_style'    => $progress_bar_style,
            ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Current step labels (in display order):',
            wp_json_encode(array_values($step_context), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Rewrite each step_name to be clear, concise, and appropriate for the form context.',
            'Return the full list in the same order with updated step names.',
        ];

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

    protected function buildReflectionPrompt(string $prompt, string $raw_content, array $errors, array $pages): string
    {
        $step_context = array_map(fn($p) => ['page_id' => $p['page_id'], 'step_name' => $p['step_name']], $pages);

        return implode("\n", [
            'Your previous step labels 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,
            'Current step labels (in display order):',
            wp_json_encode(array_values($step_context), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'Return corrected JSON only.',
        ]);
    }

    protected function validatePayload(array $decoded, array $pages): array
    {
        $errors = [];
        $step_labels = $decoded['step_labels'] ?? null;

        if (!is_array($step_labels) || empty($step_labels)) {
            $errors[] = __('Step labels response must include a non-empty step_labels array.', 'griffinforms-form-builder');
            return $errors;
        }

        $expected_count = count($pages);
        if (count($step_labels) !== $expected_count) {
            $errors[] = sprintf(
                __('Step labels response must contain exactly %d entries (one per page); got %d.', 'griffinforms-form-builder'),
                $expected_count,
                count($step_labels)
            );
            return $errors;
        }

        $valid_page_ids = array_column($pages, 'page_id');
        foreach ($step_labels as $i => $entry) {
            if (!is_array($entry)) {
                $errors[] = sprintf(__('Step label entry %d is not an object.', 'griffinforms-form-builder'), $i + 1);
                continue;
            }
            $pid = absint($entry['page_id'] ?? 0);
            if (!in_array($pid, $valid_page_ids, true)) {
                $errors[] = sprintf(__('Step label entry %d has invalid page_id %d.', 'griffinforms-form-builder'), $i + 1, $pid);
            }
            $name = trim((string) ($entry['step_name'] ?? ''));
            if ($name === '') {
                $errors[] = sprintf(__('Step label entry %d has an empty step_name.', 'griffinforms-form-builder'), $i + 1);
            }
        }

        return $errors;
    }

    protected function applyStepLabels(int $form_id, array $pages, array $step_labels, Format $format_sql): array
    {
        $label_map = [];
        foreach ($step_labels as $entry) {
            $pid = absint($entry['page_id'] ?? 0);
            if ($pid > 0) {
                $label_map[$pid] = $this->normalizeText((string) ($entry['step_name'] ?? ''), 255);
            }
        }

        $mutated_ids = [];
        foreach ($pages as $page) {
            $page_id = absint($page['page_id']);
            if (!isset($label_map[$page_id])) {
                continue;
            }

            $updated_step_data = [
                'step_name' => $label_map[$page_id],
                'step_icon' => (string) ($page['step_icon'] ?? ''),
            ];

            $format_sql->updateItemValue('formpage', $page_id, 'step_data', maybe_serialize($updated_step_data));
            $mutated_ids[] = $page_id;
        }

        return ['success' => !empty($mutated_ids), 'mutated_ids' => $mutated_ids];
    }

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