<?php
/**
 * API Image générique — Unsplash (ou Pexels) pour premier post / galerie vide
 *
 * Utilisé par le Cold Start (post Présentation, Drip-Feed sans médias).
 * Clé API : définir AVISION_UNSPLASH_ACCESS_KEY ou option avision_unsplash_access_key.
 *
 * @package Avision
 */

namespace Avision;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Avision_Image_API {

    private const UNSPLASH_SEARCH_URL = 'https://api.unsplash.com/search/photos';
    private const PEXELS_SEARCH_URL   = 'https://api.pexels.com/v1/search';
    private const PER_PAGE = 5;

    /** URLs de secours : Bureau de Luxe / premium neutre — 8 images Unsplash (bureaux modernes + boutiques). [cite: Vision Totale] */
    private const FALLBACK_IMAGE_URLS = [
        'https://images.unsplash.com/photo-1497366216548-37526070297c?w=800&q=80',
        'https://images.unsplash.com/photo-1568992687947-868a62a9f521?w=800&q=80',
        'https://images.unsplash.com/photo-1604328698692-f76ea9498e76?w=800&q=80',
        'https://images.unsplash.com/photo-1524758631624-e2822e304c36?w=800&q=80',
        'https://images.unsplash.com/photo-1441986300917-64674dd600f0?w=800&q=80',
        'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?w=800&q=80',
        'https://images.unsplash.com/photo-1441984904996-e0b6ba687e04?w=800&q=80',
        'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=800&q=80',
    ];

    /**
     * Retourne une URL d'image de secours aléatoire (8 URLs : bureaux modernes + boutiques luxe).
     * Shuffle puis première URL — garantit le "Choix 1" même en BDD vide. [cite: Vision Totale]
     *
     * @return string URL Unsplash (une des 8 du pool)
     */
    public static function get_random_fallback_url(): string {
        $urls = array_values( self::FALLBACK_IMAGE_URLS );
        shuffle( $urls );
        return $urls[0];
    }

    /**
     * Retourne une URL d'image de secours pour Cold Start / galerie vide (sans appel API).
     * Délègue à get_random_fallback_url pour la rotation.
     *
     * @return string URL aléatoire du pool fallback
     */
    public static function get_fallback_placeholder_url(): string {
        return self::get_random_fallback_url();
    }

    /**
     * Charge la clé Unsplash depuis WordPress uniquement (define wp-config ou wp_options). Ignore .env pour éviter les problèmes de chargement.
     *
     * @return string Clé ou chaîne vide
     */
    public static function get_unsplash_key_from_wp(): string {
        $key = '';
        if ( defined( 'AVISION_UNSPLASH_ACCESS_KEY' ) && AVISION_UNSPLASH_ACCESS_KEY !== '' ) {
            $key = (string) AVISION_UNSPLASH_ACCESS_KEY;
        }
        if ( $key === '' && defined( 'AVISION_UNSPLASH_ACCESS_KEY' ) ) {
            $key = (string) constant( 'AVISION_UNSPLASH_ACCESS_KEY' );
        }
        if ( $key === '' ) {
            $key = get_option( 'avision_unsplash_access_key', '' );
        }
        if ( $key === '' ) {
            $key = get_option( 'avision_unsplash_key', '' );
        }
        return is_string( $key ) ? trim( $key ) : '';
    }

    /**
     * Récupère une URL d'image générique pour une catégorie / type de business.
     * Clé : priorité WP (define / wp_options), puis .env en dernier recours.
     *
     * @param string $category Catégorie métier (ex: "restaurant", "coiffeur", "plombier")
     * @param string|null $source Optionnel (passer null). Diagnostic interne ; passage par valeur (compatible PHP 8 avec littéraux — plus de référence).
     * @param string $city Ville optionnelle ; si "London", force la recherche en anglais (marketing agency, seo expert, consultant)
     * @return string|null URL de l'image (urls.regular Unsplash) ou URL de secours si pas de clé
     */
    public static function get_generic_business_image( string $category, ?string $source = null, string $city = '' ): ?string {
        // Logger de survie : diagnostic des clés API (pour comprendre pourquoi cascade abandonne)
        $unsplash_key = self::get_unsplash_key_from_wp();
        if ( $unsplash_key === '' && function_exists( 'getenv' ) ) {
            $env = getenv( 'AVISION_UNSPLASH_ACCESS_KEY' );
            $unsplash_key = is_string( $env ) ? trim( $env ) : '';
        }
        if ( $unsplash_key === '' && isset( $_ENV['AVISION_UNSPLASH_ACCESS_KEY'] ) && is_string( $_ENV['AVISION_UNSPLASH_ACCESS_KEY'] ) ) {
            $unsplash_key = trim( $_ENV['AVISION_UNSPLASH_ACCESS_KEY'] );
        }
        if ( $unsplash_key === '' && isset( $_SERVER['AVISION_UNSPLASH_ACCESS_KEY'] ) && is_string( $_SERVER['AVISION_UNSPLASH_ACCESS_KEY'] ) ) {
            $unsplash_key = trim( $_SERVER['AVISION_UNSPLASH_ACCESS_KEY'] );
        }
        $pexels_key = self::get_pexels_key_from_wp();
        error_log( 'AVI KEY CHECK: Unsplash=' . ( $unsplash_key !== '' ? 'OK' : 'MISSING' ) . ' | Pexels=' . ( $pexels_key !== '' ? 'OK' : 'MISSING' ) );

        $query_for_log = self::get_refined_keywords( $category );
        if ( trim( $query_for_log ) === '' ) {
            $query_for_log = self::category_to_search_query( $category );
        }
        if ( trim( $query_for_log ) === '' ) {
            $query_for_log = ( trim( $category ) === '' ? 'business office premium' : 'modern office architecture business' );
        }
        if ( $city !== '' && stripos( $city, 'london' ) !== false ) {
            $london_queries = [ 'marketing agency', 'seo expert', 'consultant' ];
            $query_for_log  = $london_queries[ array_rand( $london_queries ) ];
        }
        if ( stripos( trim( $category ), 'marketing' ) !== false ) {
            $query_for_log = 'modern office architecture business';
        }

        $key = is_string( $unsplash_key ) ? trim( $unsplash_key ) : '';
        // DEBUG: retirer après test — log de la clé pour vérifier que AVISION_UNSPLASH_ACCESS_KEY est bien récupérée
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            error_log( 'AVI Unsplash Key: ' . ( $key !== '' ? ( substr( $key, 0, 4 ) . '...' . substr( $key, -4 ) ) : 'vide' ) );
        }
        if ( $key === '' ) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Unsplash] Aucune clé trouvée: constant, option, getenv et _ENV vides. Passage au failover Pexels si disponible.' );
            }
            $query = self::get_refined_keywords( $category );
            if ( trim( $query ) === '' ) {
                $query = self::category_to_search_query( $category );
            }
            if ( trim( $query ) === '' ) {
                $query = ( trim( $category ) === '' ? 'business office premium' : 'modern office architecture business' );
            }
            if ( $city !== '' && stripos( $city, 'london' ) !== false ) {
                $london_q = [ 'marketing agency', 'seo expert', 'consultant' ];
                $query    = $london_q[ array_rand( $london_q ) ];
            }
            if ( stripos( trim( $category ), 'marketing' ) !== false ) {
                $query = 'modern office architecture business';
            }
            $query = self::add_search_variation( $query );
            $pexels_result = self::fetch_pexels_image( $query );
            if ( $pexels_result['url'] !== null ) {
                if ( $source !== null ) {
                    $source = 'pexels_category';
                }
                self::log_image_source( 'Pexels' );
                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                    error_log( 'AVI Image URL: ' . $pexels_result['url'] . ' (Pexels, pas de clé Unsplash)' );
                }
                return $pexels_result['url'];
            }
            if ( strpos( $query, 'modern office architecture business' ) !== 0 ) {
                $pexels_fb = self::fetch_pexels_image( self::add_search_variation( 'modern office architecture business' ) );
                if ( $pexels_fb['url'] !== null ) {
                    if ( $source !== null ) {
                        $source = 'pexels_fallback';
                    }
                    self::log_image_source( 'Pexels' );
                    return $pexels_fb['url'];
                }
            }
            if ( $source !== null ) {
                $source = 'static';
            }
            self::log_cascade_failed();
            self::log_image_source( 'Fallback Aléatoire' );
            return self::get_random_fallback_url();
        }

        $query = self::get_refined_keywords( $category );
        if ( trim( $query ) === '' ) {
            $query = self::category_to_search_query( $category );
        }
        $debug_query = '';
        if ( defined( 'AVISION_UNSPLASH_DEBUG_QUERY' ) && AVISION_UNSPLASH_DEBUG_QUERY !== '' ) {
            $debug_query = is_string( AVISION_UNSPLASH_DEBUG_QUERY ) ? trim( AVISION_UNSPLASH_DEBUG_QUERY ) : 'basketball';
        }
        if ( $debug_query === '' && function_exists( 'getenv' ) ) {
            $env_val = getenv( 'AVISION_UNSPLASH_DEBUG_QUERY' );
            $debug_query = ( is_string( $env_val ) && trim( $env_val ) !== '' ) ? trim( $env_val ) : '';
        }
        if ( $debug_query === '' && isset( $_ENV['AVISION_UNSPLASH_DEBUG_QUERY'] ) && is_string( $_ENV['AVISION_UNSPLASH_DEBUG_QUERY'] ) && trim( $_ENV['AVISION_UNSPLASH_DEBUG_QUERY'] ) !== '' ) {
            $debug_query = trim( $_ENV['AVISION_UNSPLASH_DEBUG_QUERY'] );
        }
        if ( $debug_query === '' ) {
            $opt = get_option( 'avision_unsplash_debug_query', '' );
            $debug_query = is_string( $opt ) && trim( $opt ) !== '' ? trim( $opt ) : '';
        }
        if ( $debug_query !== '' ) {
            $query = $debug_query;
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Unsplash] Requête de test utilisée: ' . $query . ' (AVISION_UNSPLASH_DEBUG_QUERY)' );
            }
        }

        if ( trim( $query ) === '' ) {
            $query = ( trim( $category ) === '' ? 'business office premium' : 'modern office architecture business' );
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Unsplash] Mot-clé vide, requête de secours: ' . $query );
            }
        }
        if ( $city !== '' && stripos( $city, 'london' ) !== false ) {
            $london_q = [ 'marketing agency', 'seo expert', 'consultant' ];
            $query    = $london_q[ array_rand( $london_q ) ];
        }
        if ( stripos( trim( $category ), 'marketing' ) !== false ) {
            $query = 'modern office architecture business';
        }

        $query = self::add_search_variation( $query );
        error_log( 'AVI Image Debug: Query Sent = ' . $query );

        $url = self::build_unsplash_search_url( $query );
        $result = self::fetch_unsplash_image( $key, $url, $query );
        if ( $result['url'] !== null ) {
            if ( $source !== null ) {
                $source = 'unsplash_category';
            }
            self::log_image_source( 'Unsplash' );
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( 'AVI Image URL: ' . $result['url'] );
            }
            return $result['url'];
        }
        // Fallback : si la catégorie métier ne renvoie aucune image, recherche générique pour ne jamais laisser le post sans image.
        if ( strpos( $query, 'modern office architecture business' ) !== 0 ) {
            $query_fb = self::add_search_variation( 'modern office architecture business' );
            $url_fallback = self::build_unsplash_search_url( $query_fb );
            $result_fb    = self::fetch_unsplash_image( $key, $url_fallback, $query_fb );
            if ( $result_fb['url'] !== null ) {
                if ( $source !== null ) {
                    $source = 'unsplash_fallback';
                }
                self::log_image_source( 'Unsplash' );
                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                    error_log( 'AVI Image URL: ' . $result_fb['url'] );
                }
                return $result_fb['url'];
            }
        }

        // Cascade Failover : si Unsplash a échoué, tenter Pexels. [cite: 2026-02-17]
        $pexels_result = self::fetch_pexels_image( $query );
        if ( $pexels_result['url'] !== null ) {
            if ( $source !== null ) {
                $source = 'pexels_category';
            }
            self::log_image_source( 'Pexels' );
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( 'AVI Image URL: ' . $pexels_result['url'] . ' (Pexels)' );
            }
            return $pexels_result['url'];
        }
        if ( strpos( $query, 'modern office architecture business' ) !== 0 ) {
            $pexels_fb = self::fetch_pexels_image( self::add_search_variation( 'modern office architecture business' ) );
            if ( $pexels_fb['url'] !== null ) {
                if ( $source !== null ) {
                    $source = 'pexels_fallback';
                }
                self::log_image_source( 'Pexels' );
                if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                    error_log( 'AVI Image URL: ' . $pexels_fb['url'] . ' (Pexels fallback)' );
                }
                return $pexels_fb['url'];
            }
        }

        if ( $source !== null ) {
            $source = 'static';
        }
        self::log_cascade_failed();
        self::log_image_source( 'Fallback Aléatoire' );
        $fallback_url = self::get_random_fallback_url();
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            error_log( 'AVI Image URL: ' . $fallback_url . ' (fallback aléatoire)' );
        }
        return $fallback_url;
    }

    /**
     * Ajoute une variation aléatoire à la requête de recherche pour éviter d'avoir toujours la même image.
     *
     * @param string $query Requête de base (ex: "restaurant food")
     * @return string Requête avec suffixe optionnel (ex: "restaurant food pro")
     */
    private static function add_search_variation( string $query ): string {
        $query = trim( $query );
        if ( $query === '' ) {
            return 'modern office architecture business';
        }
        $suffixes = [ '', ' pro', ' premium', ' quality' ];
        $idx      = function_exists( 'wp_rand' ) ? wp_rand( 0, count( $suffixes ) - 1 ) : array_rand( $suffixes );
        return $query . $suffixes[ $idx ];
    }

    /**
     * Dictionnaire de Vision Métier : mots-clés Unsplash/Pexels optimisés par catégorie (Premium/Modern).
     * Évite les hors-sujets en mappant 9 catégories SaaS à des termes précis.
     *
     * @param string $category Catégorie métier (FR ou EN)
     * @return string Mots-clés anglais pour la recherche, ou chaîne vide si non mappé
     */
    private static function get_refined_keywords( string $category ): string {
        $category = trim( $category );
        if ( $category === '' ) {
            return '';
        }
        $lower = mb_strtolower( $category );
        $map = [
            'btp'           => 'modern carpentry, construction site, professional tools',
            'artisan'       => 'modern carpentry, construction site, professional tools',
            'construction'  => 'modern carpentry, construction site, professional tools',
            'carpentry'     => 'modern carpentry, construction site, professional tools',
            'plombier'      => 'modern carpentry, construction site, professional tools',
            'plumber'       => 'modern carpentry, construction site, professional tools',
            'menuisier'     => 'modern carpentry, construction site, professional tools',
            'beauté'        => 'luxury hair salon, spa interior, professional styling',
            'bien-être'     => 'luxury hair salon, spa interior, professional styling',
            'bien être'     => 'luxury hair salon, spa interior, professional styling',
            'coiffeur'      => 'luxury hair salon, spa interior, professional styling',
            'coiffure'      => 'luxury hair salon, spa interior, professional styling',
            'hair salon'    => 'luxury hair salon, spa interior, professional styling',
            'spa'           => 'luxury hair salon, spa interior, professional styling',
            'esthétique'    => 'luxury hair salon, spa interior, professional styling',
            'santé'         => 'modern medical clinic, dental office, professional healthcare',
            'sante'         => 'modern medical clinic, dental office, professional healthcare',
            'medical'       => 'modern medical clinic, dental office, professional healthcare',
            'médecin'       => 'modern medical clinic, dental office, professional healthcare',
            'medecin'       => 'modern medical clinic, dental office, professional healthcare',
            'dentiste'      => 'modern medical clinic, dental office, professional healthcare',
            'dentist'       => 'modern medical clinic, dental office, professional healthcare',
            'doctor'        => 'modern medical clinic, dental office, professional healthcare',
            'clinique'      => 'modern medical clinic, dental office, professional healthcare',
            'clinic'        => 'modern medical clinic, dental office, professional healthcare',
            'restauration'  => 'gourmet plate, restaurant interior, professional chef',
            'restaurant'    => 'gourmet plate, restaurant interior, professional chef',
            'café'          => 'gourmet plate, restaurant interior, professional chef',
            'cafe'          => 'gourmet plate, restaurant interior, professional chef',
            'food'          => 'gourmet plate, restaurant interior, professional chef',
            'chef'          => 'gourmet plate, restaurant interior, professional chef',
            'droit'         => 'modern law office, professional lawyer, corporate legal',
            'avocat'        => 'modern law office, professional lawyer, corporate legal',
            'lawyer'        => 'modern law office, professional lawyer, corporate legal',
            'notaire'       => 'modern law office, professional lawyer, corporate legal',
            'notary'        => 'modern law office, professional lawyer, corporate legal',
            'law office'    => 'modern law office, professional lawyer, corporate legal',
            'commerce'      => 'premium retail store, modern boutique, luxury shop interior',
            'retail'        => 'premium retail store, modern boutique, luxury shop interior',
            'boutique'      => 'premium retail store, modern boutique, luxury shop interior',
            'shop'          => 'premium retail store, modern boutique, luxury shop interior',
            'boulangerie'   => 'premium retail store, modern boutique, luxury shop interior',
            'bakery'        => 'premium retail store, modern boutique, luxury shop interior',
            'fleuriste'     => 'premium retail store, modern boutique, luxury shop interior',
            'florist'       => 'premium retail store, modern boutique, luxury shop interior',
            'hôtel'         => 'luxury hotel lobby, modern hotel room, premium hospitality',
            'hotel'         => 'luxury hotel lobby, modern hotel room, premium hospitality',
            'hébergement'   => 'luxury hotel lobby, modern hotel room, premium hospitality',
            'hospitality'   => 'luxury hotel lobby, modern hotel room, premium hospitality',
            'auto'          => 'professional car garage, auto repair workshop, modern mechanic',
            'garage'        => 'professional car garage, auto repair workshop, modern mechanic',
            'mécanicien'   => 'professional car garage, auto repair workshop, modern mechanic',
            'mecanicien'    => 'professional car garage, auto repair workshop, modern mechanic',
            'mechanic'      => 'professional car garage, auto repair workshop, modern mechanic',
            'car repair'    => 'professional car garage, auto repair workshop, modern mechanic',
            'pharmacie'     => 'modern pharmacy interior, professional healthcare retail',
            'pharmacy'      => 'modern pharmacy interior, professional healthcare retail',
            'architecte'    => 'modern architecture office, professional design studio',
            'architect'     => 'modern architecture office, professional design studio',
            'architecture'  => 'modern architecture office, professional design studio',
            'marketing'     => 'modern office architecture business',
            'consultant'    => 'modern office architecture business',
            'bureau'        => 'modern office architecture business',
            'office'        => 'modern office architecture business',
        ];
        foreach ( $map as $key => $keywords ) {
            if ( mb_strpos( $lower, $key ) !== false ) {
                return $keywords;
            }
        }
        return '';
    }

    /**
     * Construit l'URL de recherche Unsplash.
     */
    private static function build_unsplash_search_url( string $query ): string {
        return add_query_arg(
            [
                'query'       => $query,
                'per_page'    => self::PER_PAGE,
                'orientation' => 'landscape',
                'page'        => function_exists( 'wp_rand' ) ? wp_rand( 1, 10 ) : 1,
            ],
            self::UNSPLASH_SEARCH_URL
        );
    }

    /**
     * Effectue la requête HTTP Unsplash avec wp_remote_get et en-tête Authorization: Client-ID [KEY].
     *
     * @param string $key   Clé API
     * @param string $url   URL complète de recherche
     * @param string $query Libellé de la requête (pour les logs)
     * @return array{url: string|null, reason: string} URL si trouvée, sinon null + raison
     */
    private static function fetch_unsplash_image( string $key, string $url, string $query ): array {
        $response = wp_remote_get( $url, [
            'headers'   => [
                'Authorization' => 'Client-ID ' . $key,
                'Accept-Version' => 'v1',
            ],
            'timeout'   => 20,
            'sslverify' => false,
        ] );

        $code = is_wp_error( $response ) ? 0 : wp_remote_retrieve_response_code( $response );
        error_log( '[AVI IMAGE] Unsplash Return Code: ' . $code );
        if ( is_wp_error( $response ) ) {
            $err_msg = $response->get_error_message();
            error_log( '[AVI Unsplash] wp_remote_get error: ' . $err_msg );
            self::log_api_debug( 'Unsplash', 0, 'wp_error: ' . $err_msg, $query );
            self::log_image_failure( 'Unsplash', [ 'reason' => 'wp_error: ' . $err_msg, 'response_message' => $err_msg ] );
            return [ 'url' => null, 'reason' => 'wp_error: ' . $err_msg, 'response_message' => $err_msg ];
        }
        $body_raw   = wp_remote_retrieve_body( $response );
        $body_raw   = is_string( $body_raw ) ? $body_raw : '';
        $resp_msg   = function_exists( 'wp_remote_retrieve_response_message' ) ? wp_remote_retrieve_response_message( $response ) : '';
        if ( $code !== 200 ) {
            error_log( 'AVI Image Debug: HTTP error code = ' . $code . ' (Unsplash, query=' . $query . ')' );
            if ( $code === 401 || $code === 403 ) {
                error_log( 'AVI API ERROR: ' . $code );
            }
            self::log_image_failure( 'Unsplash', [ 'reason' => 'http_' . $code, 'response_message' => $resp_msg ] );
            self::log_api_debug( 'Unsplash', $code, $body_raw, $query );
            $api_message = self::parse_unsplash_error_message( $body_raw, $code );
            self::log_cron_task( 'API ERROR: ' . $api_message );
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Unsplash] API HTTP ' . $code . ': ' . substr( $body_raw, 0, 500 ) );
            }
            return [ 'url' => null, 'reason' => 'http_' . $code, 'response_message' => $resp_msg ];
        }

        $body = json_decode( $body_raw, true );
        if ( empty( $body['results'] ) || ! is_array( $body['results'] ) ) {
            error_log( 'AVI Image Debug: Empty response (results empty), HTTP 200, query=' . $query );
            self::log_image_failure( 'Unsplash', [ 'reason' => 'empty_results', 'response_message' => '' ] );
            self::log_api_debug( 'Unsplash', 200, $body_raw . ( $body_raw === '' ? '(empty body)' : '' ), $query );
            $api_message_200 = self::parse_unsplash_error_message( $body_raw, 200 );
            self::log_cron_task( 'API ERROR: ' . $api_message_200 );
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Unsplash] API 200 mais results vides pour query=' . $query . ( $query === '' ? ' (mot-clé vide)' : '' ) );
            }
            return [ 'url' => null, 'reason' => 'empty_results', 'response_message' => '' ];
        }

        $results = $body['results'];
        $count   = count( $results );
        $idx     = ( $count <= 1 ) ? 0 : ( function_exists( 'wp_rand' ) ? wp_rand( 0, $count - 1 ) : array_rand( $results ) );
        $first   = $results[ $idx ] ?? null;
        if ( ! $first || empty( $first['urls']['regular'] ) ) {
            return [ 'url' => null, 'reason' => 'no_regular_url', 'response_message' => '' ];
        }

        $image_url = is_string( $first['urls']['regular'] ) ? $first['urls']['regular'] : null;
        return [ 'url' => $image_url, 'reason' => 'ok' ];
    }

    /**
     * Charge la clé Pexels depuis WordPress uniquement (define wp-config ou wp_options). Ignore .env si problème.
     *
     * @return string Clé ou chaîne vide
     */
    public static function get_pexels_key_from_wp(): string {
        $key = '';
        if ( defined( 'AVISION_PEXELS_API_KEY' ) && AVISION_PEXELS_API_KEY !== '' ) {
            $key = (string) AVISION_PEXELS_API_KEY;
        }
        if ( $key === '' ) {
            $key = get_option( 'avision_pexels_api_key', '' );
        }
        return is_string( $key ) ? trim( $key ) : '';
    }

    /**
     * Récupère une image via l'API Pexels (failover après Unsplash).
     * Clé : priorité WP (define / wp_options), puis getenv / _ENV.
     *
     * @param string $query Requête de recherche (ex: "restaurant food pro")
     * @return array{url: string|null, reason: string}
     */
    public static function fetch_pexels_image( string $query ): array {
        $query = trim( $query );
        if ( $query === '' ) {
            $query = 'modern office architecture business';
        }
        error_log( 'AVI Image Debug: Query Sent = ' . $query . ' (Pexels)' );
        $key = self::get_pexels_key_from_wp();
        if ( $key === '' && function_exists( 'getenv' ) ) {
            $env = getenv( 'AVISION_PEXELS_API_KEY' );
            $key = is_string( $env ) ? trim( $env ) : '';
        }
        if ( $key === '' && isset( $_ENV['AVISION_PEXELS_API_KEY'] ) && is_string( $_ENV['AVISION_PEXELS_API_KEY'] ) ) {
            $key = trim( $_ENV['AVISION_PEXELS_API_KEY'] );
        }
        if ( $key === '' ) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Pexels] Aucune clé AVISION_PEXELS_API_KEY.' );
            }
            return [ 'url' => null, 'reason' => 'no_key' ];
        }

        $url = add_query_arg(
            [
                'query'    => $query,
                'per_page' => self::PER_PAGE,
                'page'     => function_exists( 'wp_rand' ) ? wp_rand( 1, 10 ) : 1,
            ],
            self::PEXELS_SEARCH_URL
        );
        // Pexels : Authorization = clé API seule (pas de "Client-ID" ni "Bearer"). Voir https://www.pexels.com/api/documentation/
        $response = wp_remote_get( $url, [
            'headers'   => [
                'Authorization' => $key,
            ],
            'timeout'   => 20,
            'sslverify' => false,
        ] );

        if ( is_wp_error( $response ) ) {
            $err_msg = $response->get_error_message();
            error_log( '[AVI IMAGE] Pexels Return Code: 0 (wp_error)' );
            error_log( '[AVI Pexels] wp_remote_get error: ' . $err_msg );
            self::log_api_debug( 'Pexels', 0, 'wp_error: ' . $err_msg, $query );
            self::log_image_failure( 'Pexels', [ 'reason' => 'wp_error: ' . $err_msg, 'response_message' => $err_msg ] );
            return [ 'url' => null, 'reason' => 'wp_error: ' . $err_msg, 'response_message' => $err_msg ];
        }
        $code      = wp_remote_retrieve_response_code( $response );
        $resp_msg  = function_exists( 'wp_remote_retrieve_response_message' ) ? wp_remote_retrieve_response_message( $response ) : '';
        error_log( '[AVI IMAGE] Pexels Return Code: ' . $code );
        $body_raw = wp_remote_retrieve_body( $response );
        $body_raw = is_string( $body_raw ) ? $body_raw : '';
        if ( $code !== 200 ) {
            error_log( 'AVI Image Debug: HTTP error code = ' . $code . ' (Pexels, query=' . $query . ')' );
            self::log_image_failure( 'Pexels', [ 'reason' => 'http_' . $code, 'response_message' => $resp_msg ] );
            self::log_api_debug( 'Pexels', $code, $body_raw, $query );
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Pexels] API HTTP ' . $code . ': ' . substr( $body_raw, 0, 300 ) );
            }
            return [ 'url' => null, 'reason' => 'http_' . $code, 'response_message' => $resp_msg ];
        }

        $body = json_decode( $body_raw, true );
        if ( empty( $body['photos'] ) || ! is_array( $body['photos'] ) ) {
            error_log( 'AVI Image Debug: Empty response (photos empty), HTTP 200, query=' . $query );
            self::log_image_failure( 'Pexels', [ 'reason' => 'empty_results', 'response_message' => '' ] );
            self::log_api_debug( 'Pexels', 200, $body_raw . ( $body_raw === '' ? '(empty body)' : '' ), $query );
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( '[AVI Pexels] API 200 mais photos vides pour query=' . $query );
            }
            return [ 'url' => null, 'reason' => 'empty_results', 'response_message' => '' ];
        }

        $photos = $body['photos'];
        $count  = count( $photos );
        $idx    = ( $count <= 1 ) ? 0 : ( function_exists( 'wp_rand' ) ? wp_rand( 0, $count - 1 ) : array_rand( $photos ) );
        $photo  = $photos[ $idx ] ?? null;
        if ( ! $photo || empty( $photo['src'] ) || ! is_array( $photo['src'] ) ) {
            return [ 'url' => null, 'reason' => 'no_src', 'response_message' => '' ];
        }
        $src = $photo['src'];
        $image_url = isset( $src['large'] ) && is_string( $src['large'] ) ? $src['large'] : ( isset( $src['medium'] ) && is_string( $src['medium'] ) ? $src['medium'] : ( isset( $src['original'] ) && is_string( $src['original'] ) ? $src['original'] : null ) );
        return [ 'url' => $image_url, 'reason' => 'ok' ];
    }

    /**
     * Traduction FR -> EN des catégories courantes pour les requêtes Unsplash/Pexels (APIs en anglais).
     *
     * @param string $category Catégorie (FR ou EN)
     * @return string Terme EN pour la recherche
     */
    private static function translate_category_fr_to_en( string $category ): string {
        $category = trim( $category );
        if ( $category === '' ) {
            return '';
        }
        $fr_to_en = [
            'restaurant'   => 'restaurant',
            'avocat'       => 'lawyer',
            'médecin'      => 'doctor',
            'coiffeur'     => 'hair salon',
            'coiffure'     => 'hair salon',
            'plombier'     => 'plumber',
            'plomberie'    => 'plumber',
            'garage'       => 'car garage',
            'pharmacie'    => 'pharmacy',
            'boulangerie'  => 'bakery',
            'boulanger'    => 'bakery',
            'architecte'   => 'architect',
            'notaire'      => 'notary',
            'dentiste'     => 'dentist',
            'cabinet'      => 'office',
            'salon'        => 'salon',
            'esthéticienne'=> 'beauty salon',
            'esthéticien'  => 'beauty salon',
            'mécanicien'   => 'mechanic',
            'fleuriste'    => 'florist',
            'pâtisserie'   => 'pastry',
            'pâtissier'    => 'pastry',
            'café'         => 'cafe',
            'bar'          => 'bar',
            'hôtel'        => 'hotel',
            'hotel'        => 'hotel',
        ];
        $lower = mb_strtolower( $category );
        foreach ( $fr_to_en as $fr => $en ) {
            if ( $lower === $fr || mb_strpos( $lower, $fr ) !== false ) {
                return $en;
            }
        }
        return $category;
    }

    /**
     * Convertit une catégorie métier en requête de recherche Unsplash/Pexels.
     * Applique d'abord la traduction FR -> EN pour les catégories courantes.
     */
    private static function category_to_search_query( string $category ): string {
        $category = trim( $category );
        if ( $category === '' ) {
            return 'business store';
        }
        $search_term = self::translate_category_fr_to_en( $category );
        if ( $search_term === '' ) {
            return 'business store';
        }
        $map = [
            'restaurant'   => 'restaurant food',
            'coiffeur'     => 'hair salon',
            'hair salon'   => 'hair salon',
            'plombier'     => 'plumber repair',
            'plumber'      => 'plumber repair',
            'garage'       => 'car garage',
            'pharmacie'    => 'pharmacy',
            'pharmacy'     => 'pharmacy',
            'boulangerie'  => 'bakery bread',
            'bakery'       => 'bakery bread',
            'médecin'      => 'medical office',
            'doctor'       => 'medical office',
            'avocat'       => 'law office',
            'lawyer'       => 'law office',
            'architecte'   => 'architecture',
            'architect'    => 'architecture',
            'basketball'   => 'basketball',
            'notaire'      => 'law office',
            'notary'       => 'law office',
            'dentiste'     => 'dentist office',
            'dentist'      => 'dentist office',
        ];
        $lower = mb_strtolower( $search_term );
        foreach ( $map as $key => $query ) {
            if ( mb_strpos( $lower, $key ) !== false ) {
                return $query;
            }
        }
        return $search_term . ' business';
    }

    /** Chemin relatif du log cron (même fichier que Maintainers). [cite: 2026-02-23] */
    private const CRON_TASK_LOG = 'logs/cron-task.log';
    /** Log debug API : réponse d'erreur complète (Invalid Key, Rate Limit, etc.) en clair. */
    private const API_DEBUG_LOG = 'logs/api-debug.log';
    /** Log source image pour diagnostic : Unsplash, Pexels ou Fallback Aléatoire. */
    private const DEBUG_LOG = 'logs/debug.log';

    /**
     * Écrit dans debug.log la source de l'image renvoyée par get_generic_business_image.
     *
     * @param string $api_name 'Unsplash' | 'Pexels' | 'Fallback Aléatoire'
     */
    private static function log_image_source( string $api_name ): void {
        $plugin_dir = defined( 'AVISION_PLUGIN_DIR' ) ? AVISION_PLUGIN_DIR : ( defined( 'AVISION_PATH' ) ? AVISION_PATH : dirname( __DIR__ ) . '/' );
        $log_path   = $plugin_dir . self::DEBUG_LOG;
        $dir        = dirname( $log_path );
        if ( ! is_dir( $dir ) ) {
            @wp_mkdir_p( $dir );
        }
        $line = sprintf( "[%s] IMAGE SOURCE: %s\n", gmdate( 'Y-m-d H:i:s' ), $api_name );
        @file_put_contents( $log_path, $line, FILE_APPEND | LOCK_EX );
    }

    /**
     * Écrit dans debug.log la raison exacte de l'échec d'une API (401 Unauthorized, blocage serveur, wp_error).
     *
     * @param string $api_name 'Unsplash' | 'Pexels'
     * @param array  $result   Tableau retourné par fetch_* : reason, response_message (optionnel)
     */
    private static function log_image_failure( string $api_name, array $result ): void {
        $plugin_dir = defined( 'AVISION_PLUGIN_DIR' ) ? AVISION_PLUGIN_DIR : ( defined( 'AVISION_PATH' ) ? AVISION_PATH : dirname( __DIR__ ) . '/' );
        $log_path   = $plugin_dir . self::DEBUG_LOG;
        $dir        = dirname( $log_path );
        if ( ! is_dir( $dir ) ) {
            @wp_mkdir_p( $dir );
        }
        $reason = isset( $result['reason'] ) ? (string) $result['reason'] : 'unknown';
        $msg    = isset( $result['response_message'] ) && $result['response_message'] !== '' ? (string) $result['response_message'] : '';
        $line   = sprintf( "[%s] %s FAILED | reason=%s | response_message=%s\n", gmdate( 'Y-m-d H:i:s' ), $api_name, $reason, $msg );
        @file_put_contents( $log_path, $line, FILE_APPEND | LOCK_EX );
    }

    /**
     * Écrit TOUTE la réponse d'erreur dans logs/api-debug.log (message "Invalid Key", "Rate Limit" en clair).
     *
     * @param string $source 'Unsplash' | 'Pexels'
     * @param int    $code   Code HTTP (0 si wp_error)
     * @param string $full_body Corps complet de la réponse
     * @param string $query  Requête envoyée
     */
    private static function log_api_debug( string $source, int $code, string $full_body, string $query = '' ): void {
        $plugin_dir = defined( 'AVISION_PLUGIN_DIR' ) ? AVISION_PLUGIN_DIR : ( defined( 'AVISION_PATH' ) ? AVISION_PATH : dirname( __DIR__ ) . '/' );
        $log_path   = $plugin_dir . self::API_DEBUG_LOG;
        $dir        = dirname( $log_path );
        if ( ! is_dir( $dir ) ) {
            @wp_mkdir_p( $dir );
        }
        $sep   = str_repeat( '-', 80 );
        $line  = sprintf( "[%s] %s | HTTP %d | query=%s\n", gmdate( 'Y-m-d H:i:s' ), $source, $code, $query );
        $line .= "FULL RESPONSE BODY:\n" . ( is_string( $full_body ) ? $full_body : '' ) . "\n" . $sep . "\n";
        @file_put_contents( $log_path, $line, FILE_APPEND | LOCK_EX );
    }

    /**
     * Log crucial : lorsque l'API renvoie l'image de secours (cascade Unsplash + Pexels échouée).
     * Écrit dans logs/api-debug.log pour diagnostic.
     */
    private static function log_cascade_failed(): void {
        $plugin_dir = defined( 'AVISION_PLUGIN_DIR' ) ? AVISION_PLUGIN_DIR : ( defined( 'AVISION_PATH' ) ? AVISION_PATH : dirname( __DIR__ ) . '/' );
        $log_path   = $plugin_dir . self::API_DEBUG_LOG;
        $dir        = dirname( $log_path );
        if ( ! is_dir( $dir ) ) {
            @wp_mkdir_p( $dir );
        }
        $line = sprintf( "[%s] CASCADE FAILED: Both APIs unreachable or invalid keys.\n", gmdate( 'Y-m-d H:i:s' ) );
        @file_put_contents( $log_path, $line, FILE_APPEND | LOCK_EX );
    }

    /**
     * Écrit une ligne dans logs/cron-task.log (erreurs API image). [cite: 2026-02-23]
     *
     * @param string $message Message à écrire (ex: "API ERROR: Rate Limit Exceeded")
     */
    private static function log_cron_task( string $message ): void {
        $plugin_dir = defined( 'AVISION_PLUGIN_DIR' ) ? AVISION_PLUGIN_DIR : ( defined( 'AVISION_PATH' ) ? AVISION_PATH : dirname( __DIR__ ) . '/' );
        $log_path   = $plugin_dir . self::CRON_TASK_LOG;
        $dir        = dirname( $log_path );
        if ( ! is_dir( $dir ) ) {
            @wp_mkdir_p( $dir );
        }
        $line = sprintf( "[%s] %s\n", gmdate( 'Y-m-d H:i:s' ), $message );
        @file_put_contents( $log_path, $line, FILE_APPEND | LOCK_EX );
    }

    /**
     * Extrait un message d'erreur lisible depuis la réponse JSON Unsplash (errors[], error, message).
     *
     * @param string $body_raw Corps brut de la réponse
     * @param int    $code     Code HTTP
     * @return string Message pour logs/cron-task.log
     */
    private static function parse_unsplash_error_message( $body_raw, int $code ): string {
        if ( ! is_string( $body_raw ) || $body_raw === '' ) {
            return 'HTTP ' . $code . ' (empty body)';
        }
        $body = json_decode( $body_raw, true );
        if ( ! is_array( $body ) ) {
            return 'HTTP ' . $code . ' — ' . substr( $body_raw, 0, 200 );
        }
        if ( ! empty( $body['errors'] ) && is_array( $body['errors'] ) ) {
            return 'HTTP ' . $code . ' — ' . implode( '; ', $body['errors'] );
        }
        if ( ! empty( $body['error'] ) && is_string( $body['error'] ) ) {
            return 'HTTP ' . $code . ' — ' . $body['error'];
        }
        if ( ! empty( $body['message'] ) && is_string( $body['message'] ) ) {
            return 'HTTP ' . $code . ' — ' . $body['message'];
        }
        return 'HTTP ' . $code . ' — ' . substr( $body_raw, 0, 200 );
    }
}
