<?php
namespace Avision;

/**
 * REST API pour le Mode Gardien
 *
 * Enregistre les endpoints REST WordPress pour le webhook
 * et les interactions avec le Mode Gardien.
 * FQCN \Avision\* partout. Classes chargées par l’autoloader Avision (avision-dashboard.php).
 */
class Guardian_REST_API {

    /**
     * Charge les dépendances Guardian. Base = __DIR__ (ce fichier est dans includes/), repli AVISION_PATH si fichier introuvable.
     *
     * @return \WP_REST_Response|null Null si tout est chargé, sinon une réponse d'erreur 500 à renvoyer.
     */
    private function ensure_guardian_deps() {
        $base = __DIR__ . DIRECTORY_SEPARATOR;
        $fallback_base = defined( 'AVISION_PATH' ) ? ( rtrim( AVISION_PATH, '/\\' ) . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR ) : '';
        $files = [
            'class-avision-database.php',
            'class-guardian-nlp-analyzer.php',
            'class-guardian-response-generator.php',
            'class-guardian-mode.php',
            'class-avision-guardian-maintainers.php',
        ];
        foreach ( $files as $file ) {
            $path = $base . $file;
            if ( ! file_exists( $path ) && $fallback_base !== '' ) {
                $path = $fallback_base . $file;
            }
            if ( file_exists( $path ) ) {
                require_once $path;
            } else {
                return new \WP_REST_Response( [ 'success' => false, 'error' => 'Fichier manquant: ' . $file ], 500 );
            }
        }
        return null;
    }

    /**
     * Enregistre les routes REST API
     */
    public function register_routes() {
        // Namespace de l'API
        $namespace = 'avision/v1';
        
        // Webhook pour recevoir les avis Google
        register_rest_route($namespace, '/webhook/review', [
            'methods' => 'POST',
            'callback' => [$this, 'handle_review_webhook'],
            'permission_callback' => '__return_true' // Public endpoint
        ]);
        
        // Activer le Mode Gardien (audit_id = profile_id pour avi_saas_profiles)
        register_rest_route($namespace, '/guardian/activate', [
            'methods' => 'POST',
            'callback' => [$this, 'activate_guardian'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => ['audit_id' => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint']]
        ]);
        
        // Désactiver le Mode Gardien
        register_rest_route($namespace, '/guardian/deactivate', [
            'methods' => 'POST',
            'callback' => [$this, 'deactivate_guardian'],
            'permission_callback' => [$this, 'check_permission']
        ]);
        
        // Obtenir le statut du Mode Gardien
        register_rest_route($namespace, '/guardian/status', [
            'methods' => 'GET',
            'callback' => [$this, 'get_guardian_status'],
            'permission_callback' => [$this, 'check_permission']
        ]);
        
        // Obtenir les statistiques
        register_rest_route($namespace, '/guardian/stats', [
            'methods' => 'GET',
            'callback' => [$this, 'get_statistics'],
            'permission_callback' => [$this, 'check_permission']
        ]);
        
        // Obtenir les avis récents
        register_rest_route($namespace, '/reviews', [
            'methods' => 'GET',
            'callback' => [$this, 'get_reviews'],
            'permission_callback' => [$this, 'check_permission']
        ]);
        
        // Dashboard complet (Level 15)
        register_rest_route($namespace, '/guardian/dashboard', [
            'methods' => 'GET',
            'callback' => [$this, 'get_dashboard_status'],
            'permission_callback' => [$this, 'check_permission']
        ]);
        
        // Tester si un avis peut être traité
        register_rest_route($namespace, '/guardian/can-process', [
            'methods' => 'POST',
            'callback' => [$this, 'can_process_review'],
            'permission_callback' => [$this, 'check_permission']
        ]);
        
        // Mode Validation : avis en attente
        register_rest_route($namespace, '/guardian/pending-reviews', [
            'methods' => 'GET',
            'callback' => [$this, 'get_pending_reviews'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => ['audit_id' => ['required' => true, 'type' => 'integer']]
        ]);
        
        // Publier une réponse validée
        register_rest_route($namespace, '/guardian/publish-response', [
            'methods' => 'POST',
            'callback' => [$this, 'publish_response'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => [
                'audit_id' => ['required' => true, 'type' => 'integer'],
                'review_id' => ['required' => true, 'type' => 'string']
            ]
        ]);
        
        // Régénérer une proposition
        register_rest_route($namespace, '/guardian/regenerate-response', [
            'methods' => 'POST',
            'callback' => [$this, 'regenerate_response'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => [
                'audit_id' => ['required' => true, 'type' => 'integer'],
                'review_id' => ['required' => true, 'type' => 'string']
            ]
        ]);

        // Régénérer le contenu d'un post (Wave 3 / Cold Start / Drip-Feed) — variante de texte via Guardian_Response_Generator. [cite: 2026-02-17]
        register_rest_route($namespace, '/guardian/regenerate-post', [
            'methods' => 'POST',
            'callback' => [$this, 'regenerate_post'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => [
                'audit_id' => ['required' => true, 'type' => 'integer'],
                'post_id'  => ['required' => true, 'type' => 'integer']
            ]
        ]);
        
        // Emails transactionnels (triggers WELCOME, TIMER_DONE, SPRINT_UNLOCK, L21_FIRST_TIME)
        register_rest_route($namespace, '/trigger-email', [
            'methods' => 'POST',
            'callback' => [$this, 'trigger_transactional_email'],
            'permission_callback' => '__return_true',
            'args' => [
                'trigger' => ['required' => true, 'type' => 'string'],
                'audit_id' => ['required' => true, 'type' => 'integer'],
                'currentScore' => ['type' => 'integer'],
                'nextSprintIndex' => ['type' => 'integer'],
                'nextSprintName' => ['type' => 'string'],
                'infusionTimer' => ['type' => 'string'],
                'isFirstTime' => ['type' => 'boolean'],
                'isGuardianActive' => ['type' => 'boolean']
            ]
        ]);
        
        // Notification 24h : avis en attente depuis trop longtemps
        register_rest_route($namespace, '/guardian/pending-overdue', [
            'methods' => 'GET',
            'callback' => [$this, 'get_pending_overdue'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => ['audit_id' => ['required' => true, 'type' => 'integer']]
        ]);

        // Posts en attente (brouillons Cold Start / Wave 3) — pour afficher "Post proposé" dans le Centre d'intervention
        register_rest_route($namespace, '/guardian/pending-posts', [
            'methods' => 'GET',
            'callback' => [$this, 'get_pending_posts'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => ['audit_id' => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint']]
        ]);

        // Publier un post validé (brouillon -> Google Business Profile)
        register_rest_route($namespace, '/guardian/publish-post', [
            'methods' => 'POST',
            'callback' => [$this, 'publish_post'],
            'permission_callback' => [$this, 'check_permission'],
            'args' => [
                'audit_id' => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint'],
                'post_id'  => ['required' => true, 'type' => 'integer', 'sanitize_callback' => 'absint'],
            ]
        ]);

        // Chat AVI : proxy vers le backend FastAPI (engine.avision.org/chat)
        register_rest_route($namespace, '/chat', [
            'methods' => 'POST',
            'callback' => [$this, 'proxy_chat'],
            'permission_callback' => '__return_true',
            'args' => [
                'query' => ['required' => false, 'type' => 'string'],
                'chatHistory' => ['required' => false, 'type' => 'array'],
                'currentLevel' => ['required' => false, 'type' => 'integer', 'default' => 1],
                'dashboard_type' => ['required' => false, 'type' => 'string', 'default' => 'premium'],
                'systemMessage' => ['required' => false, 'type' => 'string'],
            ]
        ]);

        // Catégories Google : proxy vers FastAPI (engine.avision.org/google-categories)
        register_rest_route($namespace, '/google-categories', [
            'methods' => 'GET',
            'callback' => [\Avision\Avision_API::class, 'proxy_categories_to_fastapi'],
            'permission_callback' => '__return_true',
            'args' => [
                'search' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
                'regionCode' => ['required' => false, 'type' => 'string', 'default' => 'US', 'sanitize_callback' => 'sanitize_text_field'],
                'languageCode' => ['required' => false, 'type' => 'string', 'default' => 'en', 'sanitize_callback' => 'sanitize_text_field'],
                'pageSize' => ['required' => false, 'type' => 'integer', 'default' => 100],
                'pageToken' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
            ]
        ]);
    }

    /**
     * Proxy chat : forward vers FastAPI et retourne { success, reply, model } ou { success, error, raw }.
     */
    public function proxy_chat($request) {
        $body = $request->get_json_params();
        if (empty($body) && $request->get_body()) {
            $body = json_decode($request->get_body(), true);
        }
        if (!is_array($body)) {
            $body = [];
        }
        if (empty($body['query']) && !empty($body['userMessage'])) {
            $body['query'] = $body['userMessage'];
        }
        $api = new \Avision\Avision_API();
        $api->init();
        $result = $api->proxy_chat_to_fastapi_params($body);
        return new \WP_REST_Response($result, !empty($result['success']) ? 200 : 502);
    }
    
    /**
     * Webhook pour recevoir les avis Google
     */
    public function handle_review_webhook($request) {
        $err = $this->ensure_guardian_deps();
        if ( $err !== null ) {
            return $err;
        }
        $data = $request->get_json_params();

        error_log('Guardian Webhook received: ' . print_r($data, true));

        if (empty($data)) {
            return new \WP_Error('no_data', 'No data received', ['status' => 400]);
        }

        if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
            error_log( '[AVI FATAL] Guardian_Mode class not found' );
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
        }
        $guardian = new Guardian_Mode();
        $result = $guardian->handle_review_webhook($data);
        
        if ($result['success']) {
            return rest_ensure_response($result);
        } else {
            return new \WP_Error('processing_failed', $result['error'] ?? 'Unknown error', ['status' => 500]);
        }
    }
    
    /**
     * Active le Mode Gardien.
     * FQCN \Avision\Guardian_Mode (autoloader).
     */
    public function activate_guardian( $request ) {
        $err = $this->ensure_guardian_deps();
        if ( $err !== null ) {
            return $err;
        }
        $audit_id = 0;
        try {
            $data     = method_exists( $request, 'get_json_params' ) ? $request->get_json_params() : [];
            $data     = is_array( $data ) ? $data : [];
            $audit_id = ! empty( $data['audit_id'] ) ? (int) $data['audit_id'] : (int) $request->get_param( 'audit_id' );

            if ( $audit_id <= 0 ) {
                return new \WP_REST_Response( [ 'success' => false, 'error' => 'Audit ID required (envoyez audit_id dans le body JSON ou en param)' ], 400 );
            }

            if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
                error_log( '[AVI FATAL] Guardian_Mode class not found' );
                return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
            }
            $guardian = new Guardian_Mode();
            $settings = [
                'auto_respond'         => $request->get_param( 'auto_respond' ) ?? true,
                'response_delay'       => $request->get_param( 'response_delay' ) ?? 60,
                'tone_of_voice'        => $request->get_param( 'tone_of_voice' ) ?? 'professional',
                'include_seo'          => $request->get_param( 'include_seo' ) ?? true,
                'notification_email'  => $request->get_param( 'notification_email' ) ?? true,
                'languages'            => $request->get_param( 'languages' ) ?? [ 'fr', 'en' ],
                'google_location_id'   => $request->get_param( 'google_location_id' ),
                'google_access_token'  => $request->get_param( 'google_access_token' ),
            ];
            $guardian->activate( $audit_id, $settings );

            if ( function_exists( 'avision_trigger_immediate_wave3' ) ) {
                avision_trigger_immediate_wave3( $audit_id );
            }

            return rest_ensure_response( [
                'success'     => true,
                'message'     => 'Mode Gardien synchronisé (fiche mise à jour, Wave 3 lancée)',
                'webhook_url' => get_rest_url( null, 'avision/v1/webhook/review' ),
            ] );
        } catch ( \Throwable $e ) {
            error_log( 'AVI ERROR: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() );
            try {
                if ( $audit_id > 0 && function_exists( 'avision_trigger_immediate_wave3' ) ) {
                    avision_trigger_immediate_wave3( $audit_id );
                }
            } catch ( \Throwable $e2 ) {
                error_log( 'AVI ERROR: ' . $e2->getMessage() . ' in ' . $e2->getFile() . ':' . $e2->getLine() );
            }
            return new \WP_REST_Response( [
                'success'    => false,
                'error'      => 'Erreur lors de l\'activation du Mode Gardien.',
                'message'    => ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? $e->getMessage() : null,
                'debug_hint' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ? ( $e->getMessage() . ' in ' . basename( $e->getFile() ) . ':' . $e->getLine() ) : 'Activez WP_DEBUG pour voir le détail.',
            ], 500 );
        }
    }
    
    /**
     * Désactive le Mode Gardien
     */
    public function deactivate_guardian($request) {
        $audit_id = $request->get_param('audit_id');

        if (empty($audit_id)) {
            return new \WP_Error('missing_param', 'Audit ID required', ['status' => 400]);
        }

        if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
            error_log( '[AVI FATAL] Guardian_Mode class not found' );
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
        }
        $guardian = new Guardian_Mode();
        $result = $guardian->deactivate($audit_id);
        
        if ($result) {
            return rest_ensure_response([
                'success' => true,
                'message' => 'Mode Gardien désactivé'
            ]);
        }
        
        return new \WP_Error('deactivation_failed', 'Failed to deactivate', ['status' => 500]);
    }
    
    /**
     * Obtient le statut du Mode Gardien
     */
    public function get_guardian_status($request) {
        $audit_id = $request->get_param('audit_id');

        if (empty($audit_id)) {
            return new \WP_Error('missing_param', 'Audit ID required', ['status' => 400]);
        }
        
        $database = new Avision_Database();
        $database->init();
        
        $settings = $database->get_guardian_settings($audit_id);
        
        return rest_ensure_response([
            'success' => true,
            'is_active' => $settings['is_active'] ?? false,
            'settings' => $settings,
            'webhook_url' => get_rest_url(null, 'avision/v1/webhook/review')
        ]);
    }
    
    /**
     * Obtient les statistiques
     */
    public function get_statistics($request) {
        $audit_id = $request->get_param('audit_id');

        if (empty($audit_id)) {
            return new \WP_Error('missing_param', 'Audit ID required', ['status' => 400]);
        }

        if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
            error_log( '[AVI FATAL] Guardian_Mode class not found' );
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
        }
        $guardian = new Guardian_Mode();
        $stats = $guardian->get_statistics($audit_id);
        
        return rest_ensure_response([
            'success' => true,
            'statistics' => $stats
        ]);
    }
    
    /**
     * Obtient les avis récents
     */
    public function get_reviews($request) {
        $audit_id = $request->get_param('audit_id');
        $limit = $request->get_param('limit') ?? 10;
        
        if (empty($audit_id)) {
            return new \WP_Error('missing_param', 'Audit ID required', ['status' => 400]);
        }
        
        $database = new Avision_Database();
        $database->init();
        
        $reviews = $database->get_recent_reviews($audit_id, $limit);
        
        return rest_ensure_response([
            'success' => true,
            'reviews' => $reviews
        ]);
    }
    
    /**
     * Obtient le statut complet du dashboard (Level 15)
     */
    public function get_dashboard_status($request) {
        $audit_id = $request->get_param('audit_id');

        if (empty($audit_id)) {
            return new \WP_Error('missing_param', 'Audit ID required', ['status' => 400]);
        }

        if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
            error_log( '[AVI FATAL] Guardian_Mode class not found' );
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
        }
        $guardian = new Guardian_Mode();
        $dashboard = $guardian->getDashboardStatus($audit_id);
        
        return rest_ensure_response([
            'success' => true,
            'dashboard' => $dashboard
        ]);
    }
    
    /**
     * Teste si un avis peut être traité automatiquement
     */
    public function can_process_review($request) {
        $review_data = $request->get_json_params();

        if (empty($review_data)) {
            return new \WP_Error('no_data', 'No review data provided', ['status' => 400]);
        }

        if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
            error_log( '[AVI FATAL] Guardian_Mode class not found' );
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
        }
        $guardian = new Guardian_Mode();
        $result = $guardian->canProcessReview($review_data);
        
        return rest_ensure_response([
            'success' => true,
            'result' => $result
        ]);
    }
    
    /**
     * Récupère les avis en attente de validation
     */
    public function get_pending_reviews($request) {
        $audit_id = $request->get_param('audit_id');

        if (empty($audit_id)) {
            return new \WP_Error('missing_param', 'Audit ID required', ['status' => 400]);
        }
        
        $database = new Avision_Database();
        $database->init();
        
        $reviews = $database->get_pending_reviews($audit_id);
        
        $formatted = array_map(function ($r) {
            $context = json_decode($r['context_data'] ?? '{}', true) ?: [];
            return [
                'review_id' => $r['review_id'],
                'reviewer_name' => $r['reviewer_name'],
                'rating' => (int) $r['rating'],
                'review_text' => $r['review_text'] ?? '',
                'response_text' => $r['response_text'] ?? '',
                'avi_justification' => $context['avi_justification'] ?? '',
                'received_at' => $r['received_at'] ?? $r['created_at'] ?? null
            ];
        }, $reviews);
        
        return rest_ensure_response([
            'success' => true,
            'pending' => $formatted
        ]);
    }
    
    /**
     * Publie une réponse validée sur Google
     */
    public function publish_response($request) {
        $audit_id = $request->get_param('audit_id');
        $review_id = $request->get_param('review_id');
        $response_text = $request->get_param('response_text');

        if (empty($audit_id) || empty($review_id)) {
            return new \WP_Error('missing_param', 'audit_id and review_id required', ['status' => 400]);
        }

        if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
            error_log( '[AVI FATAL] Guardian_Mode class not found' );
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
        }
        $guardian = new Guardian_Mode();
        $result = $guardian->publishValidatedResponse($review_id, $audit_id, $response_text);
        
        return rest_ensure_response($result);
    }
    
    /**
     * Régénère une proposition de réponse
     */
    public function regenerate_response($request) {
        $audit_id = $request->get_param('audit_id');
        $review_id = $request->get_param('review_id');

        if (empty($audit_id) || empty($review_id)) {
            return new \WP_Error('missing_param', 'audit_id and review_id required', ['status' => 400]);
        }

        if ( ! class_exists( \Avision\Guardian_Mode::class ) ) {
            error_log( '[AVI FATAL] Guardian_Mode class not found' );
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Mode class not found' ], 500 );
        }
        $guardian = new Guardian_Mode();
        $result = $guardian->regeneratePendingResponse($review_id, $audit_id);
        
        return rest_ensure_response($result);
    }

    /**
     * Régénère le contenu et l'image d'un post (Cold Start / Drip-Feed / Wave 3).
     * Texte via Guardian_Response_Generator ; image via get_generic_business_image (cascade Unsplash > Pexels). [cite: 2026-02-17]
     */
    public function regenerate_post( $request ) {
        $audit_id = (int) $request->get_param( 'audit_id' );
        $post_id  = (int) $request->get_param( 'post_id' );
        if ( $post_id <= 0 ) {
            $post_id = 999;
        }

        // Bunker Démo : Si c'est le post de test, on ne touche JAMAIS à la BDD
        if ( $post_id === 999 ) {
            return rest_ensure_response( [
                'success'   => true,
                'content'   => "🌟 Ravi de vous voir ici ! Découvrez AVI SEO Local Consultant à London.\n\nNous sommes là pour vous avec le sourire — notre équipe Marketing vous accueille toute l'année.\n\nÀ très bientôt ! #AvisClients #Marketing",
                'image_url' => 'https://images.unsplash.com/photo-1497366216548-37526070297c?w=800&q=80',
            ] );
        }

        try {
            $params = $request->get_json_params();
            if ( ! is_array( $params ) ) {
                $params = [];
            }
            $audit_id   = (int) ( $params['audit_id'] ?? $request->get_param( 'audit_id' ) );
            $regenerate = isset( $params['regenerate'] ) ? trim( (string) $params['regenerate'] ) : 'both';
            if ( ! in_array( $regenerate, [ 'text', 'image', 'both' ], true ) ) {
                $regenerate = 'both';
            }
            if ( $audit_id <= 0 ) {
                return rest_ensure_response( [ 'success' => false, 'error' => 'audit_id required' ] );
            }

            $err = $this->ensure_guardian_deps();
            if ( $err !== null ) {
                return $err;
            }

            global $wpdb;
            $table_posts = $wpdb->prefix . 'avi_saas_posts';
        if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_posts ) ) !== $table_posts ) {
            return rest_ensure_response( [ 'success' => false, 'error' => 'Table not found' ] );
        }
        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT id, content FROM {$table_posts} WHERE profile_id = %d AND id = %d LIMIT 1",
            $audit_id,
            $post_id
        ), ARRAY_A );
        if ( ! $row ) {
            // Verrouillage démo : post_id 999 ne doit jamais renvoyer "Post not found" — une des 8 images de secours.
            if ( $post_id === 999 ) {
                $ctx = [ 'business_name' => 'votre établissement', 'city' => 'votre ville', 'category' => 'votre métier' ];
                $table_profiles = $wpdb->prefix . 'avi_saas_profiles';
                if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_profiles ) ) === $table_profiles ) {
                    $prof = $wpdb->get_row( $wpdb->prepare(
                        "SELECT category, city, business_name FROM {$table_profiles} WHERE id = %d LIMIT 1",
                        $audit_id
                    ), ARRAY_A );
                    if ( $prof ) {
                        $ctx['city']          = isset( $prof['city'] ) ? trim( (string) $prof['city'] ) : $ctx['city'];
                        $ctx['business_name'] = isset( $prof['business_name'] ) ? trim( (string) $prof['business_name'] ) : $ctx['business_name'];
                        $ctx['category']      = isset( $prof['category'] ) ? trim( (string) $prof['category'] ) : $ctx['category'];
                    }
                }
                $content_out = "🌟 Ravi de vous voir ici ! Découvrez " . $ctx['business_name'] . " à " . $ctx['city'] . ".\n\nNous sommes là pour vous avec le sourire — notre équipe " . ( $ctx['category'] !== '' ? $ctx['category'] : 'Marketing' ) . " vous accueille toute l'année. Chaque avis compte pour nous !\n\nÀ très bientôt ! #AvisClients #Recommandation #Marketing";
                $image_url   = class_exists( \Avision\Avision_Image_API::class ) ? \Avision\Avision_Image_API::get_random_fallback_url() : '';
                return rest_ensure_response( [ 'success' => true, 'content' => $content_out, 'image_url' => $image_url ] );
            }
            return rest_ensure_response( [ 'success' => false, 'error' => 'Post not found' ] );
        }

        $ctx             = [ 'city' => '', 'business_name' => '', 'category' => '' ];
        $table_profiles  = $wpdb->prefix . 'avi_saas_profiles';
        $profiles_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_profiles ) ) === $table_profiles;
        if ( $profiles_exists ) {
            $prof = $wpdb->get_row( $wpdb->prepare(
                "SELECT category, city, business_name FROM {$table_profiles} WHERE id = %d LIMIT 1",
                $audit_id
            ), ARRAY_A );
            if ( $prof ) {
                $ctx['city']          = isset( $prof['city'] ) ? trim( (string) $prof['city'] ) : '';
                $ctx['business_name'] = isset( $prof['business_name'] ) ? trim( (string) $prof['business_name'] ) : '';
                $ctx['category']      = isset( $prof['category'] ) ? trim( (string) $prof['category'] ) : '';
            }
        }
        if ( ( $ctx['business_name'] ?? '' ) === '' && ( $ctx['city'] ?? '' ) === '' && ( $ctx['category'] ?? '' ) === '' ) {
            $ctx = [ 'business_name' => 'votre établissement', 'city' => 'votre ville', 'category' => 'votre métier' ];
        }
        $category  = $ctx['category'];
        $new_content = ( $regenerate === 'image' ) ? ( isset( $row['content'] ) ? $row['content'] : '' ) : '';
        $image_url = null;

        if ( $regenerate === 'text' || $regenerate === 'both' ) {
            if ( ! class_exists( \Avision\Guardian_Response_Generator::class ) ) {
                error_log( '[AVI FATAL] Guardian_Response_Generator class not found' );
                return new \WP_REST_Response( [ 'success' => false, 'error' => 'Guardian_Response_Generator class not found' ], 500 );
            }
            $generator   = new Guardian_Response_Generator();
            $new_content = $generator->rewrite_post_text( isset( $row['content'] ) ? $row['content'] : '', $ctx );
            $new_content = is_string( $new_content ) ? trim( $new_content ) : '';
            if ( $new_content === '' || stripos( $new_content, 'Bonjour London' ) !== false ) {
                return rest_ensure_response( [ 'success' => false, 'error' => 'Contenu non conforme (vide ou texte fantôme). Insertion en BDD interdite.' ] );
            }
            $wpdb->update(
                $table_posts,
                [ 'content' => $new_content ],
                [ 'id' => $post_id, 'profile_id' => $audit_id ],
                [ '%s' ],
                [ '%d', '%d' ]
            );
        }

        if ( $regenerate === 'image' || $regenerate === 'both' ) {
            $image_url = null;
            if ( $profiles_exists ) {
                if ( ! class_exists( \Avision\Avision_Guardian_Maintainers::class ) ) {
                    error_log( '[AVI FATAL] Avision_Guardian_Maintainers class not found' );
                }
                if ( class_exists( \Avision\Avision_Guardian_Maintainers::class ) ) {
                    $maintainers = new Avision_Guardian_Maintainers();
                    $img_result  = $maintainers->get_best_available_image( $audit_id, $category, $ctx['city'] ?? '', true );
                    $image_url   = isset( $img_result['url'] ) && $img_result['url'] !== '' ? $img_result['url'] : null;
                    if ( $image_url !== null && ( $img_result['source'] ?? '' ) === 'external' ) {
                        $table_media = $wpdb->prefix . 'avi_saas_media';
                        if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_media ) ) === $table_media ) {
                            $now = current_time( 'mysql', true );
                            $wpdb->insert( $table_media, [
                                'profile_id'   => $audit_id,
                                'file_path'    => $image_url,
                                'media_type'   => 'photo',
                                'category'     => $category !== '' ? $category : null,
                                'status'       => 'in_stock',
                                'published_at' => null,
                                'created_at'   => $now,
                            ], [ '%d', '%s', '%s', '%s', '%s', '%s', '%s' ] );
                        }
                    }
                }
            }
        }

        if ( $regenerate === 'image' && ! $new_content ) {
            $new_content = isset( $row['content'] ) ? $row['content'] : '';
        }

        // Miroir VIP : même filtre d'interception que get_pending_posts — le volet reçoit toujours le texte premium.
        $content_out = is_string( $new_content ) ? $new_content : '';
        if ( stripos( $content_out, 'Bonjour' ) !== false ) {
            $content_out = str_ireplace(
                [ 'Bonjour London !', 'Nous sommes ravis de vous accueillir' ],
                [ '🌟 Ravi de vous voir ici !', 'Découvrez notre expertise' ],
                $content_out
            );
        }
        if ( stripos( $content_out, 'Bonjour London' ) !== false || strlen( $content_out ) < 10 ) {
            $content_out = "🌟 Ravi de vous voir ici ! Découvrez " . ( isset( $ctx['business_name'] ) && $ctx['business_name'] !== '' ? $ctx['business_name'] : 'notre établissement' ) . " à " . ( isset( $ctx['city'] ) && $ctx['city'] !== '' ? $ctx['city'] : 'votre ville' ) . ".\n\nNous sommes là pour vous avec le sourire — notre équipe Marketing vous accueille toute l'année. Chaque avis compte pour nous !\n\nÀ très bientôt ! #AvisClients #Recommandation #Marketing";
        }

            return rest_ensure_response( [
                'success'   => true,
                'content'   => $content_out,
                'image_url' => $image_url,
            ] );
        } catch ( \Throwable $e ) {
            if ( function_exists( 'error_log' ) ) {
                error_log( '[AVI regenerate-post] ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() );
            }
            return rest_ensure_response( [ 'success' => false, 'error' => 'Erreur serveur (500).' ] );
        }
    }

    /**
     * Récupère les posts en attente (brouillons Cold Start / Wave 3) pour le Centre d'intervention.
     * audit_id = profile_id (avi_saas_profiles.id).
     */
    public function get_pending_posts($request) {
        $audit_id = (int) $request->get_param('audit_id');
        if ( $audit_id <= 0 ) {
            return new \WP_REST_Response( [ 'success' => true, 'pending' => [] ], 200 );
        }
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_posts';
        if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) !== $table ) {
            return rest_ensure_response( [ 'success' => true, 'pending' => [] ] );
        }
        $rows = $wpdb->get_results( $wpdb->prepare(
            "SELECT id, title, content, status, created_at FROM `{$table}` WHERE profile_id = %d AND status IN ('draft', 'pending_review') ORDER BY created_at DESC LIMIT 20",
            $audit_id
        ), ARRAY_A );
        if ( ! is_array( $rows ) ) {
            $rows = [];
        }
        $pending = array_map( function ( $r ) {
            return [
                'post_id'  => (int) ( $r['id'] ?? 0 ),
                'id'       => (int) ( $r['id'] ?? 0 ),
                'title'    => isset( $r['title'] ) ? (string) $r['title'] : 'Post proposé',
                'content'  => isset( $r['content'] ) ? (string) $r['content'] : '',
                'summary'  => isset( $r['content'] ) ? wp_trim_words( $r['content'], 30 ) : '',
                'status'   => isset( $r['status'] ) ? (string) $r['status'] : 'draft',
                'created_at' => isset( $r['created_at'] ) ? (string) $r['created_at'] : null,
            ];
        }, $rows );

        // VIP Shield : interception du texte fantôme en sortie API — remplacement forcé par le texte premium.
        $ctx = [ 'business_name' => 'notre établissement', 'city' => 'votre ville' ];
        $t_profiles = $wpdb->prefix . 'avi_saas_profiles';
        if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t_profiles ) ) === $t_profiles ) {
            $prof = $wpdb->get_row( $wpdb->prepare(
                "SELECT business_name, city FROM {$t_profiles} WHERE id = %d LIMIT 1",
                $audit_id
            ), ARRAY_A );
            if ( is_array( $prof ) ) {
                $ctx['business_name'] = isset( $prof['business_name'] ) && trim( (string) $prof['business_name'] ) !== '' ? trim( (string) $prof['business_name'] ) : $ctx['business_name'];
                $ctx['city']          = isset( $prof['city'] ) && trim( (string) $prof['city'] ) !== '' ? trim( (string) $prof['city'] ) : $ctx['city'];
            }
        }
        foreach ( $pending as &$post ) {
            $content = isset( $post['content'] ) ? (string) $post['content'] : '';
            // Interception finale : search & replace brutal sur formules fantômes (avant retour JSON)
            if ( stripos( $content, 'Bonjour' ) !== false ) {
                $post['content'] = str_ireplace(
                    [ 'Bonjour London !', 'Nous sommes ravis de vous accueillir' ],
                    [ '🌟 Ravi de vous voir ici !', 'Découvrez notre expertise' ],
                    $post['content']
                );
                $post['summary'] = wp_trim_words( $post['content'], 30 );
            }
            $content = isset( $post['content'] ) ? (string) $post['content'] : '';
            if ( stripos( $content, 'Bonjour London' ) !== false || strlen( $content ) < 10 ) {
                $post['content'] = "🌟 Ravi de vous voir ici ! Découvrez " . $ctx['business_name'] . " à " . $ctx['city'] . ".\n\nNous sommes là pour vous avec le sourire — notre équipe Marketing vous accueille toute l'année. Chaque avis compte pour nous !\n\nÀ très bientôt ! #AvisClients #Recommandation #Marketing";
                $post['summary'] = wp_trim_words( $post['content'], 30 );
            }
        }
        unset( $post );

        return rest_ensure_response( [ 'success' => true, 'pending' => $pending ] );
    }

    /**
     * Publie un post brouillon sur Google Business Profile puis marque le post comme publié.
     */
    public function publish_post( $request ) {
        $audit_id = (int) $request->get_param( 'audit_id' );
        $post_id  = (int) $request->get_param( 'post_id' );
        if ( $audit_id <= 0 || $post_id <= 0 ) {
            return new \WP_Error( 'missing_param', 'audit_id and post_id required', [ 'status' => 400 ] );
        }

        $err = $this->ensure_guardian_deps();
        if ( $err !== null ) {
            return $err;
        }

        if ( ! class_exists( \Avision\Google_My_Business_API::class ) ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'Google_My_Business_API class not found' ], 500 );
        }

        global $wpdb;
        $profiles_table = $wpdb->prefix . 'avi_saas_profiles';
        $posts_table    = $wpdb->prefix . 'avi_saas_posts';

        if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $profiles_table ) ) !== $profiles_table ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'profiles_table_not_found' ], 503 );
        }
        if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $posts_table ) ) !== $posts_table ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'posts_table_not_found' ], 503 );
        }

        $profile = $wpdb->get_row( $wpdb->prepare(
            "SELECT google_access_token, google_location_name, category FROM `{$profiles_table}` WHERE id = %d LIMIT 1",
            $audit_id
        ), ARRAY_A );
        if ( ! is_array( $profile ) ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'profile_not_found' ], 404 );
        }

        if ( class_exists( __NAMESPACE__ . '\\Avision_Guardian_Maintainers' ) && Avision_Guardian_Maintainers::is_hotel_lodging_standard_posts_restricted( $profile ) ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'hotel_category_google_post_blocked' ], 403 );
        }

        $token = isset( $profile['google_access_token'] ) ? trim( (string) $profile['google_access_token'] ) : '';
        $location_id = isset( $profile['google_location_name'] ) ? trim( (string) $profile['google_location_name'] ) : '';
        if ( $token === '' || $location_id === '' ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'google_not_connected' ], 400 );
        }

        $post = $wpdb->get_row( $wpdb->prepare(
            "SELECT id, post_type, content FROM `{$posts_table}` WHERE id = %d AND profile_id = %d LIMIT 1",
            $post_id,
            $audit_id
        ), ARRAY_A );
        if ( ! is_array( $post ) ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'post_not_found' ], 404 );
        }

        $content = isset( $post['content'] ) ? trim( (string) $post['content'] ) : '';
        if ( $content === '' ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'post_content_empty' ], 400 );
        }

        $topic = ( isset( $post['post_type'] ) && (string) $post['post_type'] === 'offer' ) ? 'OFFER' : 'STANDARD';
        $api = new Google_My_Business_API( $token, $location_id );
        $published = $api->create_local_post( $content, $topic );
        $ok = is_array( $published ) ? ! isset( $published['error'] ) : (bool) $published;

        if ( ! $ok ) {
            return new \WP_REST_Response( [ 'success' => false, 'error' => 'google_publish_failed', 'raw' => $published ], 502 );
        }

        $wpdb->update(
            $posts_table,
            [
                'status'       => 'published',
                'published_at' => current_time( 'mysql', true ),
            ],
            [ 'id' => $post_id, 'profile_id' => $audit_id ],
            [ '%s', '%s' ],
            [ '%d', '%d' ]
        );

        return rest_ensure_response( [
            'success' => true,
            'post_id' => $post_id,
            'audit_id' => $audit_id,
            'status' => 'published',
        ] );
    }

    /**
     * Récupère le nombre d'avis en attente depuis plus de 24h (pour notification dashboard)
     */
    public function get_pending_overdue($request) {
        $audit_id = $request->get_param('audit_id');
        
        if (empty($audit_id)) {
            return new \WP_Error('missing_param', 'Audit ID required', ['status' => 400]);
        }
        
        $database = new Avision_Database();
        $database->init();
        
        $overdue = $database->get_pending_reviews_older_than_24h($audit_id);
        
        return rest_ensure_response([
            'success' => true,
            'count' => count($overdue),
            'message' => count($overdue) > 0
                ? "Chef, " . count($overdue) . " client(s) attendent ta validation pour leurs avis. Ne les laissons pas sans réponse, Google surveille notre réactivité ! ⏱️"
                : null
        ]);
    }
    
    /**
     * Déclenche un email transactionnel (WELCOME, TIMER_DONE, SPRINT_UNLOCK, L21_FIRST_TIME)
     */
    public function trigger_transactional_email($request) {
        $params = $request->get_json_params();
        if (empty($params)) {
            $params = $request->get_params();
        }
        $trigger = $params['trigger'] ?? $request->get_param('trigger');
        $audit_id = (int) ($params['audit_id'] ?? $request->get_param('audit_id'));
        
        $valid_triggers = [
            'WELCOME', 'TIMER_DONE', 'SPRINT_UNLOCK', 'L21_FIRST_TIME'
        ];
        if (!in_array($trigger, $valid_triggers)) {
            return new \WP_Error('invalid_trigger', 'Invalid trigger', ['status' => 400]);
        }
        if ($audit_id <= 0) {
            return new \WP_Error('missing_audit', 'audit_id required', ['status' => 400]);
        }
        
        $tx = new Avision_Transactional_Emails();
        $next_index = min(7, max(1, (int) ($params['nextSprintIndex'] ?? $request->get_param('nextSprintIndex') ?? 1)));
        $db = new Avision_Database();
        $db->init();
        $audit = $db->get_audit_by_id($audit_id);
        $lang = $audit['language'] ?? 'fr';
        
        $context = [
            'currentScore' => $params['currentScore'] ?? $request->get_param('currentScore') ?? 0,
            'nextSprintIndex' => $next_index,
            'nextSprintName' => $params['nextSprintName'] ?? $request->get_param('nextSprintName') ?: $tx->get_next_sprint_name($next_index, $lang),
            'infusionTimer' => $params['infusionTimer'] ?? $request->get_param('infusionTimer') ?? '',
            'isFirstTime' => (bool) ($params['isFirstTime'] ?? $request->get_param('isFirstTime')),
            'isGuardianActive' => (bool) ($params['isGuardianActive'] ?? $request->get_param('isGuardianActive')),
        ];
        
        $sent = $tx->dispatch($trigger, $audit_id, $context);
        return rest_ensure_response(['success' => $sent]);
    }
    
    /**
     * Permissions Guardian : bypass absolu lecture seule rapport /report/{slug}/{id}/ (URI + Referer),
     * puis utilisateur connecté + avi_current_user_can_access_profile (pas d’ACL MySQL ici).
     *
     * @param \WP_REST_Request|null $request Toujours fourni par WordPress pour permission_callback en tableau.
     * @return bool|\WP_Error
     */
    public function check_permission( $request = null ) {
        if ( ! ( $request instanceof \WP_REST_Request ) ) {
            return false;
        }

        // TOUT EN PREMIER : bypass rapport partagé (même logique que avi_rest_check_profile_access du plugin).
        if ( function_exists( 'avi_rest_quick_profile_id_from_primary_params' ) ) {
            $quick_pid = avi_rest_quick_profile_id_from_primary_params( $request );
            if ( $quick_pid > 0 && function_exists( 'avi_premium_report_request_uri_or_referer_authorizes_profile' )
                && avi_premium_report_request_uri_or_referer_authorizes_profile( $quick_pid ) ) {
                return true;
            }
        }

        $profile_id = 0;
        if ( function_exists( 'avision_rest_extract_profile_id_from_request' ) ) {
            $profile_id = (int) avision_rest_extract_profile_id_from_request( $request );
        } else {
            $raw = $request->get_param( 'profile_id' );
            if ( null === $raw || '' === (string) $raw ) {
                $raw = $request->get_param( 'pid' );
            }
            if ( null === $raw || '' === (string) $raw ) {
                $raw = $request->get_param( 'audit_id' );
            }
            if ( is_numeric( $raw ) && (int) $raw > 0 ) {
                $profile_id = absint( $raw );
            } elseif ( is_string( $raw ) && preg_match( '/^[a-f0-9\-]{32,36}$/i', trim( $raw ) )
                && function_exists( 'avi_theme_resolve_profile_id_by_uuid' ) ) {
                $profile_id = (int) avi_theme_resolve_profile_id_by_uuid( strtolower( trim( $raw ) ) );
            }
        }

        // BYPASS ABSOLU — rapport partagé (lecture seule) : ne pas enchaîner sur avi_current_user_can_access_profile / MySQL.
        if ( $profile_id > 0 && function_exists( 'avi_premium_report_request_uri_or_referer_authorizes_profile' )
            && avi_premium_report_request_uri_or_referer_authorizes_profile( $profile_id ) ) {
            return true;
        }

        if ( ! function_exists( 'is_user_logged_in' ) || ! is_user_logged_in() ) {
            return false;
        }

        // Pas d’audit_id / profile dans la requête : exiger au moins un utilisateur connecté (les callbacks valident le reste).
        if ( $profile_id <= 0 ) {
            return function_exists( 'current_user_can' ) ? current_user_can( 'read' ) : true;
        }

        if ( function_exists( 'avi_current_user_can_access_profile' ) ) {
            return (bool) avi_current_user_can_access_profile( $profile_id );
        }

        return function_exists( 'current_user_can' ) ? current_user_can( 'read' ) : false;
    }
}
