<?php
namespace Grav\Plugin\Form\Captcha;

use Grav\Common\Grav;
use Grav\Plugin\Form\Form;
use RocketTheme\Toolbox\Event\Event;

/**
 * Central manager for captcha processing
 */
class CaptchaManager
{
    /**
     * Initialize the captcha manager
     *
     * @return void
     */
    public static function initialize(): void
    {
        // Register all default captcha providers
        CaptchaFactory::registerDefaultProviders();

        // Allow plugins to register custom captcha providers
        Grav::instance()->fireEvent('onFormRegisterCaptchaProviders');
    }

    /**
     * Process a captcha validation
     *
     * @param Form $form The form to validate
     * @param array|null $params Optional parameters
     * @return bool True if validation succeeded
     */
    public static function validateCaptcha(Form $form, $params = null): bool
    {
        // Handle case where $params is a boolean (backward compatibility)
        if (!is_array($params)) {
            $params = [];
        }

        // --- 1. Find the captcha field in the form ---
        $captchaField = null;
        $providerName = null;

        $formFields = $form->getBlueprint()->get('form/fields');
        foreach ($formFields as $fieldName => $fieldDef) {
            $fieldType = $fieldDef['type'] ?? null;

            // Check for modern captcha type with provider
            if ($fieldType === 'captcha') {
                $captchaField = $fieldDef;
                $providerName = $fieldDef['provider'] ?? 'recaptcha';
                break;
            }

            // Check for legacy type-based providers (like basic-captcha and turnstile)
            // This is for backward compatibility
            elseif ($fieldType && CaptchaFactory::hasProvider($fieldType)) {
                $captchaField = $fieldDef;
                $providerName = $fieldType;
                break;
            }
        }

        if (!$captchaField || !$providerName) {
            // No captcha field found or no provider specified
            return true;
        }

        $captchaFieldName = $captchaField['name'] ?? $fieldName;

        // --- 2. Get provider and validate ---
        $provider = CaptchaFactory::getProvider($providerName);
        if (!$provider) {
            Grav::instance()['log']->error("Form Captcha: Unknown provider '{$providerName}' requested");
            return false;
        }

        // A per-form reCAPTCHA version override comes from the blueprint field
        // definition, on the server -- never from the submitted payload. Hand it to the
        // provider as a validation parameter so it does not have to sniff the request to
        // find it (GHSA-89j6-8h38-2cc3).
        if (!isset($params['recaptcha_version']) && is_array($captchaField) && isset($captchaField['recaptcha_version'])) {
            $params = (array) $params;
            $params['recaptcha_version'] = $captchaField['recaptcha_version'];
        }

        // Allow plugins to modify the validation parameters
        $validationEvent = new Event([
            'form' => $form,
            'field' => $captchaField,
            'provider' => $providerName,
            'params' => $params
        ]);
        Grav::instance()->fireEvent('onBeforeCaptchaValidation', $validationEvent);
        $params = $validationEvent['params'];

        // Validate using the provider
        try {
            $result = $provider->validate($form->value(), $params);

            if (!$result['success']) {
                $logDetails = $result['details'] ?? [];
                $errorMessage = self::getErrorMessage($captchaField, $result['error'] ?? 'validation-failed', $providerName);

                // Fire validation error event
                Grav::instance()->fireEvent('onFormValidationError', new Event([
                    'form' => $form,
                    'message' => $errorMessage,
                    'messages' => array_merge($form->messages ?? [], [$captchaFieldName => [$errorMessage]]),
                    'provider' => $providerName
                ]));

                // Log the failure
                $uri = Grav::instance()['uri'];
                Grav::instance()['log']->warning(
                    "Form Captcha ({$providerName}) validation failed: [{$uri->route()}] Details: " .
                    json_encode($logDetails)
                );

                return false;
            }

            // Log success
            Grav::instance()['log']->info("Form Captcha ({$providerName}) validation successful for form: " . $form->name);

            // Fire success event
            Grav::instance()->fireEvent('onCaptchaValidationSuccess', new Event([
                'form' => $form,
                'provider' => $providerName
            ]));

            return true;
        } catch (\Exception $e) {
            // Handle other errors
            Grav::instance()['log']->error("Form Captcha ({$providerName}) validation error: " . $e->getMessage());

            $errorMessage = Grav::instance()['language']->translate('PLUGIN_FORM.ERROR_VALIDATING_CAPTCHA');
            Grav::instance()->fireEvent('onFormValidationError', new Event([
                'form' => $form,
                'message' => $errorMessage,
                'messages' => array_merge($form->messages ?? [], [$captchaFieldName => [$errorMessage]]),
                'provider' => $providerName,
                'exception' => $e
            ]));

            return false;
        }
    }

    /**
     * Get appropriate error message based on error code and field definition
     *
     * @param array $field Field definition
     * @param string $errorCode Error code
     * @param string $provider Provider name
     * @return string
     */
    protected static function getErrorMessage(array $field, string $errorCode, string $provider): string
    {
        $grav = Grav::instance();

        // First check for specific message in field definition. `captcha_not_validated`
        // is the canonical key; `recaptcha_not_validated` is the legacy key kept for
        // backward compatibility with older form definitions.
        if (isset($field['captcha_not_validated'])) {
            return $field['captcha_not_validated'];
        }
        if (isset($field['recaptcha_not_validated'])) {
            return $field['recaptcha_not_validated'];
        }

        // Then check for specific error code message
        if ($errorCode === 'missing-input-response') {
            return $grav['language']->translate('PLUGIN_FORM.ERROR_CAPTCHA_NOT_COMPLETED');
        }

        // Allow providers to supply custom error messages via event
        $messageEvent = new Event([
            'provider' => $provider,
            'errorCode' => $errorCode,
            'field' => $field,
            'message' => null
        ]);
        $grav->fireEvent('onCaptchaErrorMessage', $messageEvent);

        if ($messageEvent['message']) {
            return $messageEvent['message'];
        }

        // Finally fall back to generic message
        return $grav['language']->translate('PLUGIN_FORM.ERROR_VALIDATING_CAPTCHA');
    }

    /**
     * Get client-side initialization data for a captcha field
     *
     * @param string $formId Form ID
     * @param array $field Field definition
     * @return array Client properties
     */
    public static function getClientProperties(string $formId, array $field): array
    {
        $providerName = $field['provider'] ?? null;

        // Handle legacy field types as providers
        if (!$providerName && isset($field['type'])) {
            $fieldType = $field['type'];
            if (CaptchaFactory::hasProvider($fieldType)) {
                $providerName = $fieldType;
            }
        }

        if (!$providerName) {
            // Default to recaptcha for backward compatibility
            $providerName = 'recaptcha';
        }

        $provider = CaptchaFactory::getProvider($providerName);

        if (!$provider) {
            return [
                'provider' => $providerName,
                'error' => "Unknown captcha provider: {$providerName}"
            ];
        }

        return $provider->getClientProperties($formId, $field);
    }

    /**
     * Get template name for a captcha field
     *
     * @param array $field Field definition
     * @return string Template name
     */
    public static function getTemplateName(array $field): string
    {
        $providerName = $field['provider'] ?? null;

        // Handle legacy field types as providers
        if (!$providerName && isset($field['type'])) {
            $fieldType = $field['type'];
            if (CaptchaFactory::hasProvider($fieldType)) {
                $providerName = $fieldType;
            }
        }

        if (!$providerName) {
            // Default to recaptcha for backward compatibility
            $providerName = 'recaptcha';
        }

        $provider = CaptchaFactory::getProvider($providerName);

        if (!$provider) {
            return 'forms/fields/captcha/default.html.twig';
        }

        return $provider->getTemplateName();
    }
}
