<?php

declare(strict_types=1);

namespace MailerPress\Api;

\defined('ABSPATH') || exit;

use MailerPress\Core\Attributes\Endpoint;
use MailerPress\Core\Webhooks\WebhookManager;
use MailerPress\Core\Webhooks\WebhookDispatcher;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

/**
 * API endpoints pour les webhooks
 * 
 * @since 1.2.0
 */
class Webhooks
{
    /**
     * Vérifie si MailerPress Pro est actif
     * 
     * @return bool True si Pro est actif
     */
    private function isProActive(): bool
    {
        if (!function_exists('is_plugin_active')) {
            require_once \ABSPATH . 'wp-admin/includes/plugin.php';
        }

        return function_exists('is_plugin_active')
            && \is_plugin_active('mailerpress-pro/mailerpress-pro.php');
    }

    private function getManager(): WebhookManager
    {
        return WebhookManager::getInstance();
    }

    /**
     * Endpoint REST API pour recevoir des webhooks
     * 
     * Route: /wp-json/mailerpress/v1/webhooks/receive/{webhook_id}
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    #[Endpoint(
        'webhooks/receive/(?P<webhook_id>[a-zA-Z0-9_-]+)',
        methods: 'POST',
        permissionCallback: '__return_true'
    )]
    public function receiveWebhook(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        $webhookId = $request->get_param('webhook_id');

        if (empty($webhookId)) {
            return new WP_Error(
                'invalid_webhook_id',
                __('Webhook ID is required', 'mailerpress'),
                ['status' => 400]
            );
        }

        // Vérifier que la configuration du webhook existe
        $webhookConfig = $this->getManager()->getWebhookConfig($webhookId);

        if (!$webhookConfig) {
            return new WP_Error(
                'webhook_not_found',
                __('Webhook not found or has been deleted', 'mailerpress'),
                ['status' => 404]
            );
        }

        // Vérifier la signature si une clé secrète est configurée ET si une signature est fournie
        // La signature est optionnelle : si elle n'est pas fournie, on accepte la requête

        if ($webhookConfig && !empty($webhookConfig['secret'])) {
            $signature = $request->get_header('X-Webhook-Signature');
            $body = $request->get_body();

            // Si une signature est fournie, on la vérifie
            // Si aucune signature n'est fournie, on accepte la requête (signature optionnelle)
            if (!empty($signature) && !WebhookDispatcher::verifySignature($body, $signature, $webhookConfig['secret'])) {
                return new WP_Error(
                    'invalid_signature',
                    __('Invalid webhook signature', 'mailerpress'),
                    ['status' => 401]
                );
            }
        }

        // Récupérer les données du webhook
        $payload = $request->get_json_params();

        if (empty($payload)) {
            $payload = $request->get_body_params();
        }

        // Vérifier si le webhook est activé
        if ($webhookConfig && isset($webhookConfig['enabled']) && !$webhookConfig['enabled']) {
            return new WP_REST_Response([
                'success' => false,
                'message' => __('Webhook is disabled', 'mailerpress'),
                'webhook_id' => $webhookId,
            ], 200);
        }

        // Exécuter les handlers configurés pour ce webhook
        $handlerManager = \MailerPress\Core\Webhooks\IncomingWebhookHandlerManager::getInstance();
        $handlerResults = [];

        // Récupérer les handlers configurés pour ce webhook
        $configuredHandlers = $webhookConfig['handlers'] ?? [];
        $handlerConfig = $webhookConfig['handler_config'] ?? [];

        if (!empty($configuredHandlers) && is_array($configuredHandlers)) {
            // Exécuter uniquement les handlers configurés
            foreach ($configuredHandlers as $handlerId) {
                if ($handlerManager->isHandlerRegistered($handlerId)) {
                    try {
                        // Passer la configuration du handler au payload
                        $handlerPayload = $payload;
                        if (isset($handlerConfig[$handlerId])) {
                            $handlerPayload['_handler_config'] = $handlerConfig[$handlerId];
                        }

                        $result = $handlerManager->executeHandler($handlerId, $webhookId, $handlerPayload, $request);
                        $handlerResults[$handlerId] = $result;
                    } catch (\Exception $e) {
                        $handlerResults[$handlerId] = [
                            'success' => false,
                            'error' => $e->getMessage(),
                        ];
                    }
                }
            }
        }
        // Note: Si aucun handler n'est configuré, on ne fait rien (pas d'exécution automatique)
        // Cela permet de contrôler précisément quelles actions sont exécutées

        // Déclencher l'action pour que le trigger puisse l'intercepter (pour les workflows)
        do_action('mailerpress_webhook_received', $webhookId, $payload, $request);

        // Retourner une réponse de succès avec les résultats des handlers
        return new WP_REST_Response([
            'success' => true,
            'message' => __('Webhook received successfully', 'mailerpress'),
            'webhook_id' => $webhookId,
            'handlers_executed' => $handlerResults,
        ], 200);
    }

    /**
     * Endpoint pour lister les webhooks configurés (pour debug/admin)
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response
     */
    #[Endpoint(
        'webhooks',
        methods: 'GET',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function listWebhooks(WP_REST_Request $request): WP_REST_Response
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_REST_Response([
                'error' => __('Webhooks require MailerPress Pro', 'mailerpress'),
            ], 403);
        }

        $webhooks = $this->getManager()->getAllWebhookConfigs();

        // Masquer les secrets dans la réponse
        foreach ($webhooks as &$webhook) {
            if (isset($webhook['secret'])) {
                $webhook['secret'] = '***';
            }
        }

        return new WP_REST_Response($webhooks, 200);
    }

    /**
     * Endpoint pour obtenir les configurations de webhooks entrants
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response
     */
    #[Endpoint(
        'webhooks/incoming',
        methods: 'GET',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function getIncomingWebhooks(WP_REST_Request $request): WP_REST_Response
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_REST_Response([
                'error' => __('Webhooks require MailerPress Pro', 'mailerpress'),
            ], 403);
        }

        $webhooks = $this->getManager()->getAllWebhookConfigs();

        // Masquer les secrets dans la réponse
        foreach ($webhooks as &$webhook) {
            if (isset($webhook['secret'])) {
                $webhook['secret'] = '***';
            }
            // Ajouter l'URL du webhook
            if (isset($webhook['id'])) {
                $webhook['url'] = rest_url('mailerpress/v1/webhooks/receive/' . $webhook['id']);
            }
        }

        // Récupérer les handlers disponibles
        $handlerManager = \MailerPress\Core\Webhooks\IncomingWebhookHandlerManager::getInstance();
        $availableHandlers = $handlerManager->getDefaultHandlersInfo();

        return new WP_REST_Response([
            'webhooks' => $webhooks,
            'available_handlers' => $availableHandlers,
        ], 200);
    }

    /**
     * Endpoint pour sauvegarder les configurations de webhooks entrants
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    #[Endpoint(
        'webhooks/incoming',
        methods: 'POST',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function saveIncomingWebhooks(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_Error(
                'pro_required',
                __('Webhooks require MailerPress Pro', 'mailerpress'),
                ['status' => 403]
            );
        }

        $webhooks = $request->get_json_params();

        if (empty($webhooks)) {
            $webhooks = $request->get_body_params();
        }

        if (!is_array($webhooks)) {
            return new WP_Error(
                'invalid_data',
                __('Invalid webhook data', 'mailerpress'),
                ['status' => 400]
            );
        }

        // Récupérer tous les webhooks existants pour détecter les suppressions
        $existingWebhooks = $this->getManager()->getAllWebhookConfigs();
        $webhookIdsToKeep = [];

        foreach ($webhooks as $webhookId => $config) {
            if (!is_array($config)) {
                continue;
            }

            // Valider le format de l'ID
            if (!preg_match('/^[a-zA-Z0-9_-]+$/', $webhookId)) {
                continue;
            }

            $webhookIdsToKeep[] = $webhookId;

            // Préparer la configuration
            $webhookConfig = [
                'id' => $webhookId,
                'enabled' => isset($config['enabled']) ? (bool) $config['enabled'] : true,
                'description' => sanitize_text_field($config['description'] ?? ''),
                'handlers' => isset($config['handlers']) && is_array($config['handlers'])
                    ? array_map('sanitize_text_field', $config['handlers'])
                    : [],
                'handler_config' => isset($config['handler_config']) && is_array($config['handler_config'])
                    ? $config['handler_config']
                    : [],
            ];

            // Gérer le secret
            $existingConfig = $this->getManager()->getWebhookConfig($webhookId);
            // Si le secret est explicitement fourni dans la requête
            if (isset($config['secret'])) {
                $providedSecret = trim($config['secret']);

                // Si le secret fourni n'est pas vide, l'utiliser
                if (!empty($providedSecret)) {
                    $webhookConfig['secret'] = sanitize_text_field($providedSecret);
                } else {
                    // Si le secret fourni est vide, garder l'existant ou générer un nouveau
                    if ($existingConfig && !empty($existingConfig['secret'])) {
                        $webhookConfig['secret'] = $existingConfig['secret'];
                    } else {
                        // Générer un nouveau secret si aucun n'existe
                        $webhookConfig['secret'] = bin2hex(random_bytes(16));
                    }
                }
            } else {
                // Si le secret n'est pas fourni dans la requête, garder l'existant
                if ($existingConfig && !empty($existingConfig['secret'])) {
                    $webhookConfig['secret'] = $existingConfig['secret'];
                } else {
                    // Générer un nouveau secret si aucun n'existe
                    $webhookConfig['secret'] = bin2hex(random_bytes(16));
                }
            }

            $this->getManager()->registerWebhookConfig($webhookId, $webhookConfig);

            // Vérifier que le secret a bien été sauvegardé
            $savedConfig = $this->getManager()->getWebhookConfig($webhookId);
        }

        // Supprimer les webhooks qui ne sont plus dans la liste
        foreach ($existingWebhooks as $existingId => $existingConfig) {
            if (!in_array($existingId, $webhookIdsToKeep, true)) {
                // Supprimer le webhook via le manager
                $this->getManager()->deleteWebhookConfig($existingId);
            }
        }

        return new WP_REST_Response([
            'success' => true,
            'message' => __('Incoming webhook settings saved successfully', 'mailerpress'),
        ], 200);
    }

    /**
     * Endpoint pour obtenir les événements disponibles
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response
     */
    #[Endpoint(
        'webhooks/events',
        methods: 'GET',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function getEvents(WP_REST_Request $request): WP_REST_Response
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_REST_Response([
                'error' => __('Webhooks require MailerPress Pro', 'mailerpress'),
            ], 403);
        }

        $events = $this->getManager()->getEventRegistry()->getEventInfo();
        return new WP_REST_Response($events, 200);
    }

    /**
     * Endpoint pour créer ou mettre à jour un webhook
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    #[Endpoint(
        'webhooks/(?P<webhook_id>[a-zA-Z0-9_-]+)',
        methods: 'POST',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function saveWebhook(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_Error(
                'pro_required',
                __('Webhooks require MailerPress Pro', 'mailerpress'),
                ['status' => 403]
            );
        }

        $webhookId = $request->get_param('webhook_id');

        if (empty($webhookId)) {
            return new WP_Error(
                'invalid_webhook_id',
                __('Webhook ID is required', 'mailerpress'),
                ['status' => 400]
            );
        }

        // Valider le format de l'ID (lettres, chiffres, tirets, underscores uniquement)
        if (!preg_match('/^[a-zA-Z0-9_-]+$/', $webhookId)) {
            return new WP_Error(
                'invalid_webhook_id_format',
                __('Webhook ID can only contain letters, numbers, hyphens, and underscores', 'mailerpress'),
                ['status' => 400]
            );
        }

        $config = [
            'id' => $webhookId,
            'secret' => sanitize_text_field($request->get_param('secret') ?? ''),
            'enabled' => (bool) ($request->get_param('enabled') ?? true),
            'description' => sanitize_text_field($request->get_param('description') ?? ''),
        ];

        // Si le secret est vide, générer un nouveau secret
        if (empty($config['secret'])) {
            $existingConfig = $this->getManager()->getWebhookConfig($webhookId);
            if (!$existingConfig || empty($existingConfig['secret'])) {
                // Générer un nouveau secret
                $config['secret'] = bin2hex(random_bytes(16));
            } else {
                // Garder le secret existant
                $config['secret'] = $existingConfig['secret'];
            }
        }

        $this->getManager()->registerWebhookConfig($webhookId, $config);

        // Masquer le secret dans la réponse
        $responseConfig = $config;
        $responseConfig['secret'] = '***';

        return new WP_REST_Response([
            'success' => true,
            'message' => __('Webhook saved successfully', 'mailerpress'),
            'webhook' => $responseConfig,
        ], 200);
    }

    /**
     * Endpoint pour supprimer un webhook
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    #[Endpoint(
        'webhooks/(?P<webhook_id>[a-zA-Z0-9_-]+)',
        methods: 'DELETE',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function deleteWebhook(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_Error(
                'pro_required',
                __('Webhooks require MailerPress Pro', 'mailerpress'),
                ['status' => 403]
            );
        }

        $webhookId = $request->get_param('webhook_id');

        if (empty($webhookId)) {
            return new WP_Error(
                'invalid_webhook_id',
                __('Webhook ID is required', 'mailerpress'),
                ['status' => 400]
            );
        }

        $configs = $this->getManager()->getAllWebhookConfigs();

        if (!isset($configs[$webhookId])) {
            return new WP_Error(
                'webhook_not_found',
                __('Webhook not found', 'mailerpress'),
                ['status' => 404]
            );
        }

        unset($configs[$webhookId]);
        update_option('mailerpress_webhook_configs', $configs);

        return new WP_REST_Response([
            'success' => true,
            'message' => __('Webhook deleted successfully', 'mailerpress'),
        ], 200);
    }

    /**
     * Endpoint pour obtenir les configurations de webhooks sortants
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response
     */
    #[Endpoint(
        'webhooks/outgoing',
        methods: 'GET',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function getOutgoingWebhooks(WP_REST_Request $request): WP_REST_Response
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_REST_Response([
                'error' => __('Webhooks require MailerPress Pro', 'mailerpress'),
            ], 403);
        }

        $configs = $this->getManager()->getAllOutgoingWebhookConfigs();

        // S'assurer que c'est un tableau
        if (!is_array($configs)) {
            $configs = [];
        }

        // Masquer les secrets dans la réponse
        foreach ($configs as &$config) {
            if (isset($config['secret']) && !empty($config['secret'])) {
                $config['secret'] = '***';
            }
        }

        return new WP_REST_Response($configs, 200);
    }

    /**
     * Endpoint pour sauvegarder les configurations de webhooks sortants
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    #[Endpoint(
        'webhooks/outgoing',
        methods: 'POST',
        permissionCallback: [Permissions::class, 'canView']
    )]
    public function saveOutgoingWebhooks(WP_REST_Request $request): WP_REST_Response|WP_Error
    {
        // Vérifier que Pro est actif
        if (!$this->isProActive()) {
            return new WP_Error(
                'pro_required',
                __('Webhooks require MailerPress Pro', 'mailerpress'),
                ['status' => 403]
            );
        }

        // Essayer d'abord get_json_params, puis get_body_params
        $configs = $request->get_json_params();

        if (empty($configs)) {
            $configs = $request->get_body_params();
        }

        // Si toujours vide, essayer de parser le body directement
        if (empty($configs)) {
            $body = $request->get_body();
            if (!empty($body)) {
                $decoded = json_decode($body, true);
                $jsonError = json_last_error();
                if ($jsonError === JSON_ERROR_NONE && is_array($decoded)) {
                    $configs = $decoded;
                }
            }
        }

        if (!is_array($configs)) {
            return new WP_Error(
                'invalid_data',
                __('Invalid configuration data', 'mailerpress'),
                ['status' => 400]
            );
        }

        if (empty($configs)) {
            // Ne pas retourner d'erreur si c'est vide, juste logger
        }

        // Charger les configurations existantes pour préserver les secrets non modifiés
        $existingConfigs = $this->getManager()->getAllOutgoingWebhookConfigs();

        foreach ($configs as $eventKey => $config) {
            if (!is_array($config)) {
                continue;
            }

            // S'assurer que 'enabled' est un booléen strict
            $config['enabled'] = isset($config['enabled']) && ($config['enabled'] === true || $config['enabled'] === 'true' || $config['enabled'] === 1 || $config['enabled'] === '1');

            // Si le secret est '***', garder le secret existant
            if (isset($config['secret']) && $config['secret'] === '***') {
                $existingConfig = $existingConfigs[$eventKey] ?? null;
                if ($existingConfig && !empty($existingConfig['secret'])) {
                    $config['secret'] = $existingConfig['secret'];
                } else {
                    unset($config['secret']);
                }
            }

            // Valider et nettoyer les URLs
            if (isset($config['urls']) && is_array($config['urls'])) {
                $config['urls'] = array_values(array_filter(
                    array_map('esc_url_raw', $config['urls']),
                    function ($url) {
                        return !empty($url) && filter_var($url, FILTER_VALIDATE_URL);
                    }
                ));
            } else {
                $config['urls'] = [];
            }

            // Le secret n'est plus géré par événement, mais globalement
            // On supprime toute référence au secret dans la config de l'événement
            if (isset($config['secret'])) {
                unset($config['secret']);
            }

            $this->getManager()->registerOutgoingWebhookConfig($eventKey, $config);
        }

        // Attendre un peu pour s'assurer que la sauvegarde est terminée
        usleep(100000); // 100ms

        // Vérifier que les données ont bien été sauvegardées
        $savedConfigs = $this->getManager()->getAllOutgoingWebhookConfigs();

        return new WP_REST_Response([
            'success' => true,
            'message' => __('Outgoing webhook settings saved successfully', 'mailerpress'),
            'saved_count' => count($savedConfigs),
        ], 200);
    }
}
