<?php

namespace GriffinForms\Includes\Ai\PromptRegistry\Handlers;

use GriffinForms\Admin\Sql\App as SqlApp;
use GriffinForms\Includes\Ai\PromptRegistry\StructureSnapshotBuilder;

class GenerateFormThemePromptHandler extends BasePromptHandler
{
    protected const MAX_REFLECTION_RETRIES = 1;

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

    public function getUnsupportedMessage(): string
    {
        return __('I can only apply a theme to a selected form 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);
        $theme_context = $this->buildThemeContext($form_id);

        if (empty($theme_context['themes'])) {
            return $this->formatFailure(
                __('No themes are available to choose from.', '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, $theme_context, $selected_context, $request_context)
                : $this->buildReflectionPrompt($prompt, $raw_content, $last_errors, $theme_context, $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[] = __('Theme response must decode to an object.', 'griffinforms-form-builder');
            } else {
                $validation_errors = $this->validatePayload($decoded, $theme_context);
            }

            if (empty($validation_errors) && is_array($decoded)) {
                $theme_id = absint($decoded['theme_id'] ?? 0);
                $current_theme_id = (int) ($theme_context['current_theme_id'] ?? 0);
                if ($theme_id <= 0 || ($current_theme_id > 0 && $theme_id === $current_theme_id)) {
                    $validation_errors[] = __('Theme response must select a valid theme ID that is different from the current theme when one exists.', 'griffinforms-form-builder');
                } else {
                    $update_result = $this->applyThemeSelection($form_id, $theme_id);
                    if (!empty($update_result['success'])) {
                        $justification = $this->normalizeText((string) ($decoded['justification'] ?? ''), 140);
                        $message = (string) ($update_result['message'] ?? __('Applied a new theme to the form.', 'griffinforms-form-builder'));
                        if ($justification !== '') {
                            $message .= ' ' . $justification;
                        }

                        return [
                            'success' => true,
                            'status' => 'applied',
                            'message' => $message,
                            'mutated_ids' => [(int) $form_id],
                            'prompt_key' => $this->getPromptKey(),
                            'patch_plan' => [
                                'theme_id' => $theme_id,
                                'justification' => $justification,
                                'current_theme_id' => (int) $theme_context['current_theme_id'],
                                'available_themes' => $theme_context['themes'],
                            ],
                        ];
                    }

                    return $this->formatFailure(
                        (string) ($update_result['message'] ?? __('Griffin Assist could not safely apply the selected theme.', 'griffinforms-form-builder')),
                        'theme_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 select a safe replacement theme. Try applying a specific theme manually.', 'griffinforms-form-builder'),
            'theme_validation_failed',
            422,
            ['request_context' => $request_context]
        );
    }

    protected function buildSystemPrompt(): string
    {
        return implode("\n", [
            'You are Griffin Assist in theme selection mode for GriffinForms.',
            'Return JSON only.',
            'Exact shape: {"theme_id":123,"justification":"string","success_message":"string"}',
            'Select one theme ID from the provided theme list.',
            'You must choose a different theme ID than the current theme ID when one exists.',
            'Choose the most suitable theme by matching the form name, form heading, and form description against the theme name and theme description.',
            'Prefer a theme whose naming and description best match the form\'s purpose and tone.',
            'Also provide a very short justification explaining why this theme fits the form, ideally one sentence under 120 characters.',
            'Do not return theme names, explanations, markdown, or code fences.',
            'Do not return the current theme ID when one exists.',
        ]);
    }

    protected function buildUserPrompt(string $prompt, array $theme_context, array $selected_context, array $request_context): string
    {
        return implode("\n", [
            'User request:',
            $prompt,
            'Theme selection context:',
            wp_json_encode([
                'current_theme_id' => (int) ($theme_context['current_theme_id'] ?? 0),
                'current_theme_name' => (string) ($theme_context['current_theme_name'] ?? ''),
                'form_name' => (string) ($theme_context['form_name'] ?? ''),
                'form_heading' => (string) ($theme_context['form_heading'] ?? ''),
                'form_description' => (string) ($theme_context['form_description'] ?? ''),
                'themes' => (array) ($theme_context['themes'] ?? []),
            ], 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 one suitable theme_id from the available themes list.',
            'Also return a very short justification for the choice, ideally one sentence under 120 characters.',
        ]);
    }

    protected function buildReflectionPrompt(string $prompt, string $raw_content, array $errors, array $theme_context, array $selected_context, array $request_context): string
    {
        return implode("\n", [
            'Your previous theme selection 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,
            'Theme selection context:',
            wp_json_encode([
                'current_theme_id' => (int) ($theme_context['current_theme_id'] ?? 0),
                'current_theme_name' => (string) ($theme_context['current_theme_name'] ?? ''),
                'form_name' => (string) ($theme_context['form_name'] ?? ''),
                'form_heading' => (string) ($theme_context['form_heading'] ?? ''),
                'form_description' => (string) ($theme_context['form_description'] ?? ''),
                'themes' => (array) ($theme_context['themes'] ?? []),
            ], 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 valid theme_id from the available themes list.',
            'Also return a very short justification for the choice, ideally one sentence under 120 characters.',
        ]);
    }

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

        $theme_id = absint($decoded['theme_id'] ?? 0);
        if ($theme_id <= 0) {
            $errors[] = __('Theme response must include a valid theme_id.', 'griffinforms-form-builder');
        }

        $justification = $this->normalizeText((string) ($decoded['justification'] ?? ''), 140);
        if ($justification === '') {
            $errors[] = __('Theme response must include a short justification.', 'griffinforms-form-builder');
        }

        $current_theme_id = (int) ($theme_context['current_theme_id'] ?? 0);
        if ($theme_id > 0 && $current_theme_id > 0 && $theme_id === $current_theme_id) {
            $errors[] = __('Theme response must not reuse the current theme ID when one exists.', 'griffinforms-form-builder');
        }

        $available_ids = array_map('absint', array_column((array) ($theme_context['themes'] ?? []), 'id'));
        if ($theme_id > 0 && !in_array($theme_id, $available_ids, true)) {
            $errors[] = __('Theme response must select an ID from the available theme list.', 'griffinforms-form-builder');
        }

        return $errors;
    }

    protected function buildThemeContext(int $form_id): array
    {
        $sql = SqlApp::getInstance();
        $current_theme_id = absint($sql->getItemValue($form_id, 'form_theme', 'form'));
        $current_theme_name = '';
        if ($current_theme_id > 0) {
            $current_theme = $sql->getFormThemeById($current_theme_id);
            if (is_object($current_theme) && !empty($current_theme->name)) {
                $current_theme_name = (string) $current_theme->name;
            }
        }

        $themes = [];
        $rows = $sql->getFormThemesByCategory('all', 0, 999);
        if (is_array($rows)) {
            foreach ($rows as $row) {
                $theme_id = absint(is_object($row) ? ($row->id ?? 0) : (is_array($row) ? ($row['id'] ?? 0) : 0));
                if ($theme_id <= 0 || $theme_id === $current_theme_id) {
                    continue;
                }

                $themes[] = [
                    'id' => $theme_id,
                    'name' => (string) (is_object($row) ? ($row->name ?? '') : (is_array($row) ? ($row['name'] ?? '') : '')),
                    'description' => (string) (is_object($row) ? ($row->description ?? '') : (is_array($row) ? ($row['description'] ?? '') : '')),
                    'category' => (string) (is_object($row) ? ($row->category ?? '') : (is_array($row) ? ($row['category'] ?? '') : '')),
                ];
            }
        }

        $structure = (new StructureSnapshotBuilder())->buildFromForm($form_id);

        return [
            'current_theme_id' => $current_theme_id,
            'current_theme_name' => $current_theme_name,
            'form_name' => (string) ($structure['form']['name'] ?? ''),
            'form_heading' => (string) ($structure['form']['heading'] ?? ''),
            'form_description' => (string) ($structure['form']['description'] ?? ''),
            'themes' => $themes,
        ];
    }

    protected function applyThemeSelection(int $form_id, int $theme_id): array
    {
        $sql = SqlApp::getInstance();
        $updated = $sql->updateItemValue('form', $form_id, 'form_theme', $theme_id);
        if ($updated === false) {
            return [
                'success' => false,
                'message' => __('Failed to update the form theme.', 'griffinforms-form-builder'),
            ];
        }

        return [
            'success' => true,
            'message' => __('Applied a new theme to the form.', 'griffinforms-form-builder'),
        ];
    }
}
