<?php
/**
 * Plugin Name: Etchenet Reviews for Google Business
 * Plugin URI: https://wordpress.org/plugins/etchenet-reviews-for-google-business/
 * Description: Display Google reviews in a carousel, floating badge, and side panel. Manual reviews, CSV/Excel import, and API sync.
 * Version: 6.1.4
 * Author: Etchenet
 * Author URI: https://profiles.wordpress.org/etchenet/
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: etchenet-reviews-for-google-business
 * Domain Path: /languages
 * Requires at least: 5.0
 * Requires PHP: 7.4
 */

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

/** Cargar traducciones: plugin primero, luego wp-content/languages/plugins si existe. */
if (!function_exists('etn_egr_load_textdomain')) {
    function etn_egr_load_textdomain() {
        $domain = 'etchenet-reviews-for-google-business';
        $locale = determine_locale();
        $plugin_dir = plugin_dir_path(__FILE__);
        $lang_dir  = $plugin_dir . 'languages';
        // 1) Cargar desde el plugin (prioridad) – .l10n.php (WP 6.5+) o .mo
        $mofile = $lang_dir . '/etchenet-reviews-for-google-business-' . $locale . '.mo';
        $l10n   = $lang_dir . '/etchenet-reviews-for-google-business-' . $locale . '.l10n.php';
        if (file_exists($l10n)) {
            load_textdomain($domain, $l10n);
        }
        if (file_exists($mofile)) {
            load_textdomain($domain, $mofile);
        }
        // On WordPress.org, translations are loaded automatically (WP 4.6+). No load_plugin_textdomain() needed.
    }
}
add_action('init', 'etn_egr_load_textdomain', 0);
add_action('admin_init', 'etn_egr_load_textdomain', 0);

// Opciones y rutas compartidas
define('ETN_EGR_OPTION_DATA', 'etn_egr_stored_data');
define('ETN_EGR_OPTION_STYLES', 'etn_egr_style_options');
define('ETN_EGR_OPTION_MANUAL_REVIEWS', 'etn_egr_manual_reviews');
define('ETN_EGR_OPTION_REVIEWS_ORDER', 'etn_egr_reviews_order_manual');
define('ETN_EGR_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('ETN_EGR_PLUGIN_URL', plugin_dir_url(__FILE__));

if (!function_exists('etn_egr_migrate_legacy_options')) {
    function etn_egr_migrate_legacy_options() {
        $option_map = [
            'grp_stored_data' => ETN_EGR_OPTION_DATA,
            'grp_style_options' => ETN_EGR_OPTION_STYLES,
            'grp_manual_reviews' => ETN_EGR_OPTION_MANUAL_REVIEWS,
            'grp_reviews_order_manual' => ETN_EGR_OPTION_REVIEWS_ORDER,
            'grp_api_key' => 'etn_egr_api_key',
            'grp_place_id' => 'etn_egr_place_id',
            'grp_enable_cron' => 'etn_egr_enable_cron',
        ];
        foreach ($option_map as $old => $new) {
            $legacy = get_option($old, null);
            $current = get_option($new, null);
            if (null !== $legacy && null === $current) {
                update_option($new, $legacy);
            }
        }
    }
}
// Prefer WebP icon when available; fallback to SVG.
$etn_egr_icon_rel = file_exists(ETN_EGR_PLUGIN_DIR . 'assets/google-color-svgrepo-com.webp')
    ? 'assets/google-color-svgrepo-com.webp'
    : 'assets/google-color-svgrepo-com.svg';
define('ETN_EGR_GOOGLE_ICON_URL', ETN_EGR_PLUGIN_URL . $etn_egr_icon_rel);
/** URL de la imagen de perfil por defecto (reseñas manuales sin foto). */
define('ETN_EGR_DEFAULT_AVATAR_URL', ETN_EGR_GOOGLE_ICON_URL);

if (!function_exists('etn_egr_normalize_review_text')) {
    /**
     * Normaliza el texto de una reseña para que la longitud en caracteres sea fiable.
     * Decodifica entidades HTML, quita etiquetas, unifica espacios y elimina caracteres invisibles.
     *
     * @param string $raw_text Texto bruto (puede tener HTML).
     * @return string Texto en UTF-8, una sola línea, sin HTML.
     */
    function etn_egr_normalize_review_text($raw_text) {
        if (!is_string($raw_text) || $raw_text === '') {
            return '';
        }
        $t = html_entity_decode($raw_text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $t = wp_strip_all_tags($t);
        $t = preg_replace('/[\r\n\t\s]+/u', ' ', $t);
        $t = trim($t);
        $t = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}\x{00AD}]/u', '', $t);
        // Asegurar UTF-8 válido para que mb_strlen/mb_substr den siempre el mismo resultado
        if (function_exists('mb_convert_encoding')) {
            $t = (string) mb_convert_encoding($t, 'UTF-8', 'UTF-8');
        }
        return $t;
    }
}

if (!function_exists('etn_egr_make_excerpt')) {
    /**
     * Recorta el texto al número exacto de caracteres indicado. Misma longitud en todas las tarjetas.
     *
     * @param string $normalized_text Texto ya normalizado (etn_egr_normalize_review_text).
     * @param int    $excerpt_len     Número de caracteres (mín. 1). Mismo valor para todas las reseñas.
     * @return array{0: string, 1: bool} [texto a mostrar, si hay que mostrar "Leer más"]
     */
    function etn_egr_make_excerpt($normalized_text, $excerpt_len) {
        $excerpt_len = max(1, (int) $excerpt_len);
        if ($normalized_text === '') {
            return array('', false);
        }
        $len = function_exists('mb_strlen') ? mb_strlen($normalized_text, 'UTF-8') : strlen($normalized_text);
        if ($len <= $excerpt_len) {
            return array($normalized_text, false);
        }
        $display = function_exists('mb_substr') ? mb_substr($normalized_text, 0, $excerpt_len, 'UTF-8') : substr($normalized_text, 0, $excerpt_len);
        // Comprobar por longitud: si lo mostrado es más corto que el completo, siempre "Leer más"
        $display_len = function_exists('mb_strlen') ? mb_strlen($display, 'UTF-8') : strlen($display);
        $needs_more = ($display_len < $len);
        return array($display, $needs_more);
    }
}

if (!function_exists('etn_egr_resolve_review_photo_url')) {
    /**
     * Devuelve la URL de la foto de perfil de una reseña.
     * Si tiene profile_photo_id (biblioteca de medios), resuelve desde el attachment.
     *
     * @param array $review Reseña con profile_photo_id y/o profile_photo_url.
     * @return string URL de la imagen o vacío.
     */
    function etn_egr_resolve_review_photo_url($review) {
        if (empty($review)) {
            return '';
        }
        $id = isset($review['profile_photo_id']) ? (int) $review['profile_photo_id'] : 0;
        if ($id > 0) {
            $url = wp_get_attachment_image_url($id, 'thumbnail');
            if ($url) {
                return $url;
            }
        }
        return isset($review['profile_photo_url']) ? (string) $review['profile_photo_url'] : '';
    }
}

if (!function_exists('etn_egr_get_all_reviews')) {
    /**
     * Devuelve todas las reseñas ordenadas según la opción (fecha, valoración u orden manual).
     * Cada reseña tiene la misma estructura que las tarjetas; las de Google tienen clave etn_egr_key = 'g_'.time, las manuales id.
     */
    function etn_egr_get_all_reviews() {
        $manual = get_option(ETN_EGR_OPTION_MANUAL_REVIEWS, []);
        if (!is_array($manual)) {
            $manual = [];
        }
        $data = get_option(ETN_EGR_OPTION_DATA, []);
        $api  = [];
        if (is_array($data) && isset($data['reviews']) && is_array($data['reviews'])) {
            $api = $data['reviews'];
        }
        $list = [];
        foreach ($manual as $m) {
            $key = isset($m['id']) ? $m['id'] : 'm_' . (isset($m['time']) ? $m['time'] : '');
            $m['etn_egr_key'] = $key;
            $m['profile_photo_url'] = etn_egr_resolve_review_photo_url($m);
            $list[] = $m;
        }
        foreach ($api as $a) {
            $a['etn_egr_key'] = 'g_' . (isset($a['time']) ? (int) $a['time'] : 0);
            $list[] = $a;
        }
        // Solo devolver reseñas visibles en el front (carrusel y panel)
        $list = array_values(array_filter($list, function ($r) {
            return isset($r['visible']) ? (bool) $r['visible'] : true;
        }));
        $s       = get_option(ETN_EGR_OPTION_STYLES, etn_egr_get_default_styles());
        $order   = isset($s['reviews_order']) ? $s['reviews_order'] : 'date_desc';
        $order_manual = get_option(ETN_EGR_OPTION_REVIEWS_ORDER, []);
        if (!is_array($order_manual)) {
            $order_manual = [];
        }
        if ($order === 'manual' && !empty($order_manual)) {
            $by_key = [];
            foreach ($list as $r) {
                $by_key[$r['etn_egr_key']] = $r;
            }
            $sorted = [];
            foreach ($order_manual as $key) {
                if (isset($by_key[$key])) {
                    $sorted[] = $by_key[$key];
                    unset($by_key[$key]);
                }
            }
            foreach ($by_key as $r) {
                $sorted[] = $r;
            }
            $list = $sorted;
        } else {
            usort($list, function ($a, $b) use ($order) {
                $t1 = isset($a['time']) ? (int) $a['time'] : 0;
                $t2 = isset($b['time']) ? (int) $b['time'] : 0;
                $r1 = isset($a['rating']) ? (float) $a['rating'] : 0;
                $r2 = isset($b['rating']) ? (float) $b['rating'] : 0;
                switch ($order) {
                    case 'date_asc':
                        return $t1 - $t2;
                    case 'rating_desc':
                        if ($r2 !== $r1) {
                            return $r2 > $r1 ? 1 : -1;
                        }
                        return $t2 - $t1;
                    case 'rating_asc':
                        if ($r1 !== $r2) {
                            return $r1 > $r2 ? 1 : -1;
                        }
                        return $t2 - $t1;
                    default:
                        return $t2 - $t1;
                }
            });
        }
        return array_map(function ($r) {
            unset($r['etn_egr_key']);
            return $r;
        }, $list);
    }
}

if (!function_exists('etn_egr_get_default_styles')) {
    /**
     * Valores por defecto del panel de ajustes.
     */
    function etn_egr_get_default_styles() {
        return [
            'columns'           => 3,
            'max_reviews'       => 10,
            'equal_height'      => 1,
            'card_aspect_ratio' => '16_9',
            'card_padding'      => 16,
            'card_gap'          => 12,
            'card_read_more_gap' => 4,
            'excerpt_length'    => 150,
            'autoplay'       => 1,
            'speed'          => 3000,
            'arrows'         => 1,
            'arrows_circle'  => 1,
            'arrow_bg_color' => '#ffffff',
            'arrow_icon_color' => '#555555',
            'arrow_size'     => 40,
            'arrow_pos_h'    => 'limit',
            'arrow_pos_v'    => 'middle',
            'dots'           => 1,
            'bg_card'        => 'rgba(6, 30, 70, 0.66)',
            'c_name'         => '#FDFDFD',
            'c_text'         => '#FDFDFD',
            'c_star'         => '#ffb400',
            'border_radius'  => 15,
            'font_size'      => 14,
            'show_avatar'    => 1,
            'show_date'      => 1,
            'show_google_logo' => 1,
            'show_read_more' => 1,
            'show_badge'     => 1,
            'badge_pos'      => 'right',
            'badge_m_side'   => 30,
            'badge_m_bottom' => 30,
            'badge_padding'  => 15,
            'min_stars'      => 4,
            'exclude_empty'  => 1,
            'rich_snippets'  => 1,
            // Panel lateral (sidebar)
            'sidebar_width'  => 400,
            'sidebar_bg'     => 'rgba(6, 30, 70, 0.66)',
            'sidebar_padding' => 20,
            'sidebar_border_radius' => 0,
            'sidebar_shadow' => '0 4px 20px rgba(0,0,0,0.3)',
            'sidebar_card_bg' => 'rgba(6, 30, 70, 0.66)',
            'sidebar_card_padding' => 15,
            'sidebar_card_margin' => 10,
            'sidebar_card_border_radius' => 8,
            'sidebar_max_height'     => '80vh',
            'sidebar_header_sticky'  => 1,
            'sidebar_max_reviews'    => 0,
            'write_review_url'       => '',
            'reviews_order'          => 'date_desc',
        ];
    }
}

/**
 * Encola estilos y scripts del front (carrusel/panel). Solo se llama cuando hay shortcode o badge.
 *
 * @param bool $with_swiper Si true, incluye Swiper (solo necesario cuando hay shortcode del carrusel).
 */
if (!function_exists('etn_egr_enqueue_front_assets')) {
    function etn_egr_enqueue_front_assets($with_swiper = false) {
        $s = get_option(ETN_EGR_OPTION_STYLES, etn_egr_get_default_styles());
        if ($with_swiper) {
            wp_enqueue_style('etn-egr-swiper-css', ETN_EGR_PLUGIN_URL . 'assets/swiper-bundle.min.css', [], '11.2.10');
            wp_enqueue_script('etn-egr-swiper-js', ETN_EGR_PLUGIN_URL . 'assets/swiper-bundle.min.js', [], '11.2.10', true);
        }
        wp_enqueue_style('etn-egr-style', ETN_EGR_PLUGIN_URL . 'assets/style.css', [], '6.1.4');
        wp_enqueue_script(
            'etn-egr-js',
            ETN_EGR_PLUGIN_URL . 'assets/script.js',
            $with_swiper ? ['etn-egr-swiper-js'] : [],
            '6.1.4',
            true
        );
        wp_localize_script('etn-egr-js', 'etnEgrSettings', [
            'columns'  => (int) $s['columns'],
            'autoplay' => (int) $s['autoplay'],
            'speed'    => (int) $s['speed'],
            'arrows'   => (int) $s['arrows'],
            'dots'     => (int) $s['dots'],
            'card_gap' => (int) isset($s['card_gap']) ? $s['card_gap'] : 12,
        ]);
    }
}

// Programar sincronización automática diaria al activar el plugin
if (!function_exists('etn_egr_schedule_daily_sync')) {
    function etn_egr_schedule_daily_sync() {
        if (!wp_next_scheduled('etn_egr_daily_sync')) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', 'etn_egr_daily_sync');
        }
    }
}

// Limpiar evento programado al desactivar el plugin
if (!function_exists('etn_egr_clear_daily_sync')) {
    function etn_egr_clear_daily_sync() {
        $timestamp = wp_next_scheduled('etn_egr_daily_sync');
        if ($timestamp) {
            wp_unschedule_event($timestamp, 'etn_egr_daily_sync');
        }
    }
}

/** Remove legacy translation files (g-reviews-pro-*) so only etchenet-reviews-for-google-business-* remain. */
if (!function_exists('etn_egr_cleanup_old_locale_files')) {
    function etn_egr_cleanup_old_locale_files() {
        $dir = ETN_EGR_PLUGIN_DIR . 'languages';
        if (!is_dir($dir)) {
            return;
        }
        $legacy_prefixes = array('g-reviews-pro-', 'g-reviews-pro-engine-ultra-');
        $extensions      = array('po', 'mo', 'l10n.php');
        foreach ($legacy_prefixes as $prefix) {
            foreach ($extensions as $ext) {
                $glob_pattern = $dir . DIRECTORY_SEPARATOR . $prefix . '*.' . $ext;
                foreach (glob($glob_pattern) as $path) {
                    if (function_exists('wp_delete_file')) {
                        wp_delete_file($path);
                    } else {
                        // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink -- WP < 5.2.1 fallback
                        @unlink($path);
                    }
                }
            }
        }
    }
}

register_activation_hook(__FILE__, function () {
    etn_egr_migrate_legacy_options();
    etn_egr_schedule_daily_sync();
    etn_egr_cleanup_old_locale_files();
});
register_deactivation_hook(__FILE__, 'etn_egr_clear_daily_sync');

// Carga modular
require_once ETN_EGR_PLUGIN_DIR . 'includes/class-egr-admin.php';
require_once ETN_EGR_PLUGIN_DIR . 'includes/class-egr-shortcode.php';
require_once ETN_EGR_PLUGIN_DIR . 'includes/class-egr-badge.php';

// Bootstrap
new ETN_EGR_Admin();
new ETN_EGR_Shortcode();
new ETN_EGR_Badge();

// Una sola vez: borrar archivos de idioma con prefijo antiguo para que Loco deje de mostrar "PO manquant"
add_action('admin_init', function () {
    etn_egr_migrate_legacy_options();
    if (get_option('etn_egr_old_locale_cleanup_done')) {
        return;
    }
    if (function_exists('etn_egr_cleanup_old_locale_files')) {
        etn_egr_cleanup_old_locale_files();
    }
    update_option('etn_egr_old_locale_cleanup_done', 1);
}, 1);