<?php

declare(strict_types=1);

namespace ESI_PEPPOL\controllers;

use ESI_PEPPOL\models\PEPPOL_Main_model;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

/**
 * Contrôleur pour gérer la réception des webhooks depuis l'API ESI Peppol.
 *
 * Les webhooks permettent de recevoir des notifications automatiques
 * lorsque le statut d'un document change.
 */
class PEPPOL_Webhook_controller {

    /**
     * Enregistre les routes REST API pour les webhooks.
     *
     * @return void
     */
    public static function register_routes(): void {
        // Route POST pour recevoir les webhooks
        register_rest_route(
            'esi-peppol/v1',
            '/webhook',
            [
                'methods'             => 'POST',
                'callback'            => [self::class, 'handle_webhook'],
                'permission_callback' => [self::class, 'permission_check'],
                'args'                => [
                    'event' => [
                        'required'          => true,
                        'type'              => 'string',
                        'validate_callback' => [self::class, 'validate_event'],
                    ],
                ],
            ]
        );

        // Route GET pour tester/vérifier l'endpoint
        register_rest_route(
            'esi-peppol/v1',
            '/webhook',
            [
                'methods'             => 'GET',
                'callback'            => [self::class, 'handle_webhook_get'],
                'permission_callback' => '__return_true',
            ]
        );
    }

    /**
     * Vérifie les permissions pour accéder à l'endpoint webhook.
     *
     * Les webhooks sont authentifiés via les headers personnalisés
     * X-ESIPeppol-Event, X-ESIPeppol-Document-ID, X-ESIPeppol-Company-ID.
     *
     * @param WP_REST_Request $request Requête REST.
     * @return bool|WP_Error
     */
    public static function permission_check(WP_REST_Request $request) {
        // Vérifier la présence des headers requis
        $event_header      = $request->get_header('X-ESIPeppol-Event');
        $document_id_header = $request->get_header('X-ESIPeppol-Document-ID');
        $company_id_header  = $request->get_header('X-ESIPeppol-Company-ID');

        if (empty($event_header) || empty($document_id_header) || empty($company_id_header)) {
            return new WP_Error(
                'missing_headers',
                __('Headers webhook manquants.', 'esi_peppol'),
                ['status' => 400]
            );
        }

        // Optionnel : vérifier que le Company-ID correspond à celui configuré
        // Pour l'instant, on accepte tous les webhooks avec les headers requis
        return true;
    }

    /**
     * Valide le type d'événement webhook.
     *
     * @param string          $value   Valeur à valider.
     * @param WP_REST_Request $request Requête REST.
     * @param string          $param   Nom du paramètre.
     * @return bool|WP_Error
     */
    public static function validate_event($value, WP_REST_Request $request, string $param) {
        $allowed_events = ['document.sent', 'document.error', 'document.completed'];

        if (!in_array($value, $allowed_events, true)) {
            return new WP_Error(
                'invalid_event',
                sprintf(
                    /* translators: %s: liste des événements autorisés */
                    __('Type d\'événement invalide. Événements autorisés : %s', 'esi_peppol'),
                    implode(', ', $allowed_events)
                ),
                ['status' => 400]
            );
        }

        return true;
    }

    /**
     * Traite la réception d'un webhook.
     *
     * @param WP_REST_Request $request Requête REST contenant les données du webhook.
     * @return WP_REST_Response|WP_Error
     */
    public static function handle_webhook(WP_REST_Request $request) {
        // Récupérer les headers
        $event      = $request->get_header('X-ESIPeppol-Event');
        $document_id = $request->get_header('X-ESIPeppol-Document-ID');
        $company_id  = $request->get_header('X-ESIPeppol-Company-ID');

        // Récupérer le body JSON
        $body = $request->get_json_params();

        if (empty($body)) {
            return new WP_Error(
                'invalid_body',
                __('Corps de la requête invalide ou vide.', 'esi_peppol'),
                ['status' => 400]
            );
        }

        // Valider la structure du payload
        if (!isset($body['event']) || !isset($body['document'])) {
            return new WP_Error(
                'invalid_payload',
                __('Structure du payload invalide. Champs requis : event, document.', 'esi_peppol'),
                ['status' => 400]
            );
        }

        // Vérifier la cohérence entre le header et le body
        if ($body['event'] !== $event) {
            return new WP_Error(
                'event_mismatch',
                __('Le type d\'événement dans le header ne correspond pas à celui du body.', 'esi_peppol'),
                ['status' => 400]
            );
        }

        // Extraire les données du document
        $document_data = $body['document'];
        
        // Le document_id peut être dans document.document_id (UUID) ou document.id (entier)
        // On privilégie document_id (UUID) car c'est celui stocké dans notre base
        $document_id_from_body = '';
        if (isset($document_data['document_id']) && !empty($document_data['document_id'])) {
            $document_id_from_body = (string) $document_data['document_id'];
        } elseif (isset($document_data['id'])) {
            // Fallback sur l'ID numérique si document_id n'est pas disponible
            $document_id_from_body = (string) $document_data['id'];
        }

        // Utiliser le document_id du body si disponible, sinon celui du header
        $final_document_id = !empty($document_id_from_body) ? $document_id_from_body : $document_id;

        // Log de réception du webhook
        if (class_exists(PEPPOL_Plugin::class)) {
            PEPPOL_Plugin::write_debug_file(
                [
                    'event'           => 'webhook_received',
                    'event_type'      => $event,
                    'document_id'     => $final_document_id,
                    'company_id'      => $company_id,
                    'payload'         => $body,
                ],
                'INFO'
            );
        }

        // Traiter selon le type d'événement
        $result = self::process_webhook_event($event, $final_document_id, $body);

        if (is_wp_error($result)) {
            // Log de l'erreur
            if (class_exists(PEPPOL_Plugin::class)) {
                PEPPOL_Plugin::write_debug_file(
                    [
                        'event'      => 'webhook_error',
                        'event_type' => $event,
                        'document_id' => $final_document_id,
                        'error'      => $result->get_error_message(),
                        'error_code' => $result->get_error_code(),
                    ],
                    'ERROR'
                );
            }

            return $result;
        }

        // Réponse de succès (code 200-299 requis par la documentation)
        return new WP_REST_Response(
            [
                'success' => true,
                'message' => __('Webhook traité avec succès.', 'esi_peppol'),
            ],
            200
        );
    }

    /**
     * Traite un événement webhook spécifique.
     *
     * @param string $event        Type d'événement (document.sent, document.error, document.completed).
     * @param string $document_id   ID du document.
     * @param array  $payload      Données complètes du webhook.
     * @return bool|WP_Error
     */
    private static function process_webhook_event(string $event, string $document_id, array $payload) {
        $document_data = $payload['document'] ?? [];

        // Déterminer le nouveau statut selon l'événement
        $new_status = null;
        if ($event === 'document.sent') {
            $new_status = 'sent';
        } elseif ($event === 'document.error') {
            $new_status = 'error';
        } elseif ($event === 'document.completed') {
            $new_status = 'completed';
        }

        if ($new_status === null) {
            return new WP_Error(
                'unknown_event',
                sprintf(
                    /* translators: %s: type d'événement */
                    __('Type d\'événement inconnu : %s', 'esi_peppol'),
                    $event
                ),
                ['status' => 400]
            );
        }

        // Extraire le peppol_document_id si disponible
        $peppol_document_id = isset($document_data['peppol_document_id'])
            ? (string) $document_data['peppol_document_id']
            : '';

        // Extraire le message d'erreur si présent
        $message = '';
        if ($event === 'document.error' && isset($payload['error']['message'])) {
            $message = (string) $payload['error']['message'];
        }

        // Mettre à jour le document dans la base de données
        $updated = PEPPOL_Main_model::update_by_document_id(
            $document_id,
            $new_status,
            $peppol_document_id,
            $message,
            $payload
        );

        if (!$updated) {
            // Document non trouvé - ce n'est pas forcément une erreur critique
            // (le document pourrait avoir été créé manuellement dans l'interface ESI Peppol)
            if (class_exists(PEPPOL_Plugin::class)) {
                PEPPOL_Plugin::write_debug_file(
                    [
                        'event'      => 'webhook_document_not_found',
                        'document_id' => $document_id,
                        'status'     => $new_status,
                        'message'    => __('Document non trouvé dans la base de données WordPress. Il a peut-être été créé manuellement dans l\'interface ESI Peppol.', 'esi_peppol'),
                    ],
                    'WARNING'
                );
            }

            // On retourne quand même true car le webhook a été reçu et traité
            // (même si le document n'existe pas dans notre base)
            return true;
        }

        // Log de succès
        if (class_exists(PEPPOL_Plugin::class)) {
            PEPPOL_Plugin::write_debug_file(
                [
                    'event'             => 'webhook_processed',
                    'event_type'        => $event,
                    'document_id'       => $document_id,
                    'new_status'        => $new_status,
                    'peppol_document_id' => $peppol_document_id,
                ],
                'INFO'
            );
        }

        return true;
    }

    /**
     * Gère les requêtes GET sur l'endpoint webhook.
     * Utile pour tester/vérifier que l'endpoint est accessible.
     *
     * @param WP_REST_Request $request Requête REST.
     * @return WP_REST_Response
     */
    public static function handle_webhook_get(WP_REST_Request $request) {
        return new WP_REST_Response(
            [
                'success' => true,
                'message' => __('Endpoint webhook ESI Peppol actif.', 'esi_peppol'),
                'endpoint' => 'esi-peppol/v1/webhook',
                'methods' => ['GET', 'POST'],
                'webhook_url' => self::get_webhook_url(),
            ],
            200
        );
    }

    /**
     * Retourne l'URL complète de l'endpoint webhook.
     *
     * Cette URL doit être configurée dans l'interface d'administration Filament
     * de l'API ESI Peppol.
     *
     * @return string URL du webhook.
     */
    public static function get_webhook_url(): string {
        $rest_url = rest_url('esi-peppol/v1/webhook');

        /**
         * Filtre pour modifier l'URL du webhook.
         *
         * @param string $rest_url URL REST par défaut.
         */
        return (string) apply_filters('esi_peppol_webhook_url', $rest_url);
    }
}
