<?php
/**
 * Mainteneurs du Mode Gardien — Vague 3 (Maintenance & Fraîcheur)
 *
 * Trois boucliers de maintenance :
 *   4. Image Cleaner    — Détection photos clients non conformes / basse résolution
 *   5. Drip Feed        — Publication automatique des photos en file d'attente (1 / 7 jours)
 *   6. Infinite Posts   — Recyclage des posts (réécriture + republication si > 7 jours)
 *
 * Réutilise les classes existantes sans duplication :
 *   - Google_My_Business_API → get_customer_photos(), create_media_item(), get_local_posts(), create_local_post()
 *   - Guardian_Response_Generator → apply_temperature_variation() (réécritures de posts)
 *   - Table avi_saas_media  → file d'attente Drip Feed (status in_stock → published)
 *   - Table avi_saas_posts  → historique posts Google (type update/offer)
 *
 * @package Avision
 */

namespace Avision;

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

class Avision_Guardian_Maintainers {

    private const LOG_FILE        = 'logs/guardian-maintainers.log';
    private const DRIP_INTERVAL   = 7;   // jours entre chaque publication photo
    private const POST_INTERVAL   = 7;   // jours entre chaque post Google
    private const MIN_WIDTH       = 720; // largeur min acceptable (pixels)
    private const MIN_HEIGHT      = 480; // hauteur min acceptable (pixels)

    // ──────────────────────────────────────────────
    // Point d'entrée
    // ──────────────────────────────────────────────

    /**
     * Exécute les 3 boucliers de maintenance pour un utilisateur.
     *
     * @param int   $user_id
     * @param array $profile  Ligne complète avi_saas_profiles
     * @return array{cleaned: int, drip: int, posts: int}
     */
    public function run_wave3( int $user_id, array $profile ): array {
        $results = [ 'cleaned' => 0, 'drip' => 0, 'posts' => 0 ];

        $token       = trim( (string) ( $profile['google_access_token'] ?? '' ) );
        $location_id = trim( (string) ( $profile['google_location_name'] ?? '' ) );

        if ( $token === '' || $location_id === '' ) {
            $this->log( 'DEBUG', "Wave3: skip user={$user_id} (token ou location manquant)" );
            return $results;
        }

        $profile_id = (int) ( $profile['id'] ?? 0 );

        // Bouclier 4 — Image Cleaner
        $results['cleaned'] = $this->process_image_cleaner( $user_id, $profile );

        // Bouclier 5 — Drip Feed
        $results['drip'] = $this->manage_drip_feed( $user_id, $profile );

        // Bouclier 6 — Infinite Posts
        $results['posts'] = $this->handle_infinite_posts( $user_id, $profile );

        $this->log( 'INFO', "Wave3 terminée: user={$user_id} cleaned={$results['cleaned']}, drip={$results['drip']}, posts={$results['posts']}" );
        return $results;
    }

    // ══════════════════════════════════════════════
    // BOUCLIER 4 — Image Cleaner
    // ══════════════════════════════════════════════

    /**
     * Récupère les photos clients et génère des alertes pour celles
     * de faible résolution ou signalées non conformes par l'API.
     *
     * @param int   $user_id
     * @param array $profile
     * @return int  Nombre d'alertes générées
     */
    public function process_image_cleaner( int $user_id, array $profile ): int {
        $token       = trim( (string) ( $profile['google_access_token'] ?? '' ) );
        $location_id = trim( (string) ( $profile['google_location_name'] ?? '' ) );

        if ( ! class_exists( __NAMESPACE__ . '\Google_My_Business_API' ) ) {
            $this->log( 'ERROR', 'Image Cleaner: Google_My_Business_API introuvable' );
            return 0;
        }

        $api    = new Google_My_Business_API( $token, $location_id );
        $photos = $api->get_customer_photos( 30 );

        if ( empty( $photos ) ) {
            $this->log( 'DEBUG', "Image Cleaner: aucune photo client pour user={$user_id}" );
            return 0;
        }

        $alerts     = 0;
        $cache_key  = 'avi_image_cleaner_' . (int) ( $profile['id'] ?? 0 );
        $known_ids  = get_option( $cache_key, [] );
        $new_ids    = [];

        foreach ( $photos as $photo ) {
            $media_name = $photo['name'] ?? '';
            $media_key  = md5( $media_name );

            $new_ids[] = $media_key;

            if ( in_array( $media_key, $known_ids, true ) ) {
                continue;
            }

            $flagged = false;
            $reason  = '';

            // Vérification de la résolution
            $width  = (int) ( $photo['dimensions']['widthPixels']  ?? 0 );
            $height = (int) ( $photo['dimensions']['heightPixels'] ?? 0 );

            if ( $width > 0 && $height > 0 && ( $width < self::MIN_WIDTH || $height < self::MIN_HEIGHT ) ) {
                $flagged = true;
                $reason  = "Résolution faible ({$width}x{$height}px, min " . self::MIN_WIDTH . 'x' . self::MIN_HEIGHT . ')';
            }

            // Vérification du statut Google (rejeté, en cours de modération)
            $state = $photo['googleUpdatedTime'] ?? '';
            $view  = $photo['viewCount'] ?? null;

            if ( isset( $photo['insights']['viewCount'] ) && (int) $photo['insights']['viewCount'] === 0 && ! empty( $state ) ) {
                $flagged = true;
                $reason  = $reason !== '' ? $reason . ' + possible rejet Google' : 'Photo possiblement rejetée par Google (0 vues)';
            }

            if ( ! $flagged ) {
                continue;
            }

            $reviewer   = $photo['attribution']['profileName'] ?? 'Client';
            $source_url = $photo['thumbnailUrl'] ?? $photo['sourceUrl'] ?? '';

            $msg_fr = "Image Cleaner : photo de « {$reviewer} » signalée. {$reason}.";
            $msg_en = "Image Cleaner: photo from \"{$reviewer}\" flagged. {$reason}.";

            if ( $source_url !== '' ) {
                $msg_fr .= " URL : {$source_url}";
                $msg_en .= " URL: {$source_url}";
            }

            $this->write_security_log( $user_id, 'real', 12, 'alert', $msg_fr, $msg_en );
            $alerts++;
            $this->log( 'ALERT', "Image Cleaner: {$reason} — photo par « {$reviewer} » (user={$user_id})" );
        }

        update_option( $cache_key, $new_ids, false );

        return $alerts;
    }

    // ══════════════════════════════════════════════
    // BOUCLIER 5 — Drip Feed (Photos)
    // ══════════════════════════════════════════════

    /**
     * Vérifie la dernière photo publiée. Si > DRIP_INTERVAL jours,
     * publie la photo suivante depuis la file d'attente avi_saas_media.
     *
     * @param int   $user_id
     * @param array $profile
     * @return int  1 si une photo a été publiée, 0 sinon
     */
    public function manage_drip_feed( int $user_id, array $profile ): int {
        $profile_id  = (int) ( $profile['id'] ?? 0 );
        $token       = trim( (string) ( $profile['google_access_token'] ?? '' ) );
        $location_id = trim( (string) ( $profile['google_location_name'] ?? '' ) );
        $pilot_mode  = strtolower( trim( (string) ( $profile['pilot_posts_mode'] ?? 'manual' ) ) );

        if ( $pilot_mode !== 'auto' ) {
            $this->log( 'DEBUG', "Drip Feed: mode manuel, skip user={$user_id}" );
            return 0;
        }

        if ( ! $this->is_drip_due( $profile_id ) ) {
            return 0;
        }

        $next_media = $this->get_next_media_in_queue( $profile_id );
        if ( ! $next_media ) {
            $this->log( 'DEBUG', "Drip Feed: file d'attente vide pour profile={$profile_id}" );
            return 0;
        }

        $file_path   = $next_media['file_path'] ?? '';
        $category    = $next_media['category'] ?? '';
        $description = $category !== '' ? $category : 'Photo ' . ( $next_media['media_type'] ?? 'photo' );

        if ( ! class_exists( __NAMESPACE__ . '\Google_My_Business_API' ) ) {
            $this->log( 'ERROR', 'Drip Feed: Google_My_Business_API introuvable' );
            return 0;
        }

        $api    = new Google_My_Business_API( $token, $location_id );
        $result = $api->create_media_item( $file_path, 'ADDITIONAL', $description );

        if ( $result && ! isset( $result['error'] ) ) {
            $this->mark_media_published( (int) $next_media['id'] );
            $this->log( 'OK', "Drip Feed: photo publiée (media_id={$next_media['id']}, profile={$profile_id})" );
            return 1;
        }

        $this->log( 'WARN', "Drip Feed: publication échouée (media_id={$next_media['id']}, profile={$profile_id})" );
        return 0;
    }

    /**
     * Vérifie si une publication drip-feed est due (dernière publication > DRIP_INTERVAL jours).
     */
    private function is_drip_due( int $profile_id ): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_media';

        $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $table_exists !== $table ) {
            $wpdb_alt = $wpdb->prefix . 'avi_saas_media';
            $last = $wpdb->get_var( $wpdb->prepare(
                "SELECT MAX(published_at) FROM {$wpdb_alt} WHERE profile_id = %d AND status = 'published'",
                $profile_id
            ) );

            if ( ! $last ) {
                return true;
            }

            $diff = ( time() - strtotime( $last ) ) / 86400;
            return $diff >= self::DRIP_INTERVAL;
        }

        $last = $wpdb->get_var( $wpdb->prepare(
            "SELECT MAX(published_at) FROM {$table} WHERE profile_id = %d AND status = 'published'",
            $profile_id
        ) );

        if ( ! $last ) {
            return true;
        }

        $diff = ( time() - strtotime( $last ) ) / 86400;
        return $diff >= self::DRIP_INTERVAL;
    }

    /**
     * Récupère le prochain média en file d'attente (status = 'in_stock', le plus ancien).
     */
    private function get_next_media_in_queue( int $profile_id ): ?array {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_media';

        $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $table_exists !== $table ) {
            return null;
        }

        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$table} WHERE profile_id = %d AND status = 'in_stock' ORDER BY created_at ASC LIMIT 1",
            $profile_id
        ), ARRAY_A );

        return is_array( $row ) ? $row : null;
    }

    /**
     * Marque un média comme publié dans avi_saas_media.
     */
    private function mark_media_published( int $media_id ): void {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_media';

        $wpdb->update(
            $table,
            [
                'status'       => 'published',
                'published_at' => current_time( 'mysql', true ),
            ],
            [ 'id' => $media_id ],
            [ '%s', '%s' ],
            [ '%d' ]
        );
    }

    // ══════════════════════════════════════════════
    // BOUCLIER 6 — Infinite Posts
    // ══════════════════════════════════════════════

    /**
     * Si aucun post n'a été publié depuis POST_INTERVAL jours,
     * récupère le dernier post "Offre" ou "Actualité",
     * le réécrit via Guardian_Response_Generator et le republie.
     *
     * @param int   $user_id
     * @param array $profile
     * @return int  1 si un post a été recyclé, 0 sinon
     */
    public function handle_infinite_posts( int $user_id, array $profile ): int {
        $profile_id  = (int) ( $profile['id'] ?? 0 );
        $token       = trim( (string) ( $profile['google_access_token'] ?? '' ) );
        $location_id = trim( (string) ( $profile['google_location_name'] ?? '' ) );
        $pilot_mode  = strtolower( trim( (string) ( $profile['pilot_posts_mode'] ?? 'manual' ) ) );

        if ( ! $this->is_post_due( $profile_id ) ) {
            return 0;
        }

        $source_post = $this->get_last_recyclable_post( $profile_id );
        if ( ! $source_post ) {
            $this->log( 'DEBUG', "Infinite Posts: aucun post recyclable pour profile={$profile_id}" );
            return 0;
        }

        $original_content = $source_post['content'] ?? '';
        if ( trim( $original_content ) === '' ) {
            return 0;
        }

        // Réécriture via Guardian_Response_Generator (variation de température)
        $rewritten = $this->rewrite_post_content( $original_content );

        if ( $pilot_mode === 'auto' ) {
            $published = $this->publish_post_to_google( $token, $location_id, $rewritten, $source_post );
            if ( $published ) {
                $this->save_recycled_post( $user_id, $profile_id, $source_post, $rewritten, 'published' );
                $this->log( 'OK', "Infinite Posts: post recyclé et publié (source_id={$source_post['id']}, profile={$profile_id})" );
                return 1;
            }
            $this->log( 'WARN', "Infinite Posts: publication échouée, sauvegarde en brouillon" );
        }

        // Co-pilote ou échec publication → brouillon
        $this->save_recycled_post( $user_id, $profile_id, $source_post, $rewritten, 'draft' );
        $this->log( 'OK', "Infinite Posts: brouillon créé (source_id={$source_post['id']}, profile={$profile_id})" );
        return 1;
    }

    /**
     * Vérifie si un post est dû (aucun post publié depuis POST_INTERVAL jours).
     */
    private function is_post_due( int $profile_id ): bool {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_posts';

        $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $table_exists !== $table ) {
            return true;
        }

        $last = $wpdb->get_var( $wpdb->prepare(
            "SELECT MAX(published_at) FROM {$table} WHERE profile_id = %d AND status = 'published'",
            $profile_id
        ) );

        if ( ! $last ) {
            return true;
        }

        $diff = ( time() - strtotime( $last ) ) / 86400;
        return $diff >= self::POST_INTERVAL;
    }

    /**
     * Récupère le dernier post recyclable (type 'update' ou 'offer', tout statut sauf 'rejected').
     */
    private function get_last_recyclable_post( int $profile_id ): ?array {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_posts';

        $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $table_exists !== $table ) {
            return null;
        }

        $row = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$table}
             WHERE profile_id = %d
               AND post_type IN ('update','offer')
               AND status != 'rejected'
             ORDER BY created_at DESC
             LIMIT 1",
            $profile_id
        ), ARRAY_A );

        return is_array( $row ) ? $row : null;
    }

    /**
     * Réécrit le contenu d'un post en utilisant Guardian_Response_Generator
     * pour varier la formulation (anti-duplicate Google).
     */
    private function rewrite_post_content( string $original ): string {
        if ( class_exists( __NAMESPACE__ . '\Guardian_Response_Generator' ) ) {
            $generator = new Guardian_Response_Generator();
            if ( method_exists( $generator, 'rewrite_variation' ) ) {
                return $generator->rewrite_variation( $original );
            }
        }

        // Fallback : variation manuelle légère
        return $this->manual_rewrite( $original );
    }

    /**
     * Réécriture de secours si Guardian_Response_Generator ne fournit pas
     * de méthode rewrite_variation. Applique des substitutions synonymiques
     * simples pour éviter le duplicata.
     */
    private function manual_rewrite( string $text ): string {
        $replacements = [
            'Découvrez'           => 'Profitez de',
            'profitez de'         => 'ne manquez pas',
            'Venez'               => 'Rejoignez-nous pour',
            'N\'hésitez pas'      => 'N\'attendez plus',
            'Contactez-nous'      => 'Appelez-nous dès maintenant',
            'notre équipe'        => 'nos experts',
            'nos services'        => 'nos prestations',
            'Nouveau'             => 'Nouveauté',
            'Offre spéciale'      => 'Offre exclusive',
            'offre spéciale'      => 'offre exclusive',
            'Bonne nouvelle'      => 'Excellente nouvelle',
            'bonne nouvelle'      => 'excellente nouvelle',
            'Nous sommes ravis'   => 'Nous avons le plaisir',
            'nous sommes ravis'   => 'nous avons le plaisir',
            'de vous annoncer'    => 'de vous présenter',
            'Rendez-vous'         => 'On vous attend',
        ];

        $rewritten = $text;
        $applied   = 0;

        foreach ( $replacements as $search => $replace ) {
            if ( mb_stripos( $rewritten, $search ) !== false && $applied < 3 ) {
                $rewritten = preg_replace( '/' . preg_quote( $search, '/' ) . '/ui', $replace, $rewritten, 1 );
                $applied++;
            }
        }

        $suffixes = [
            "\n\n📍 Retrouvez-nous sur Google Maps !",
            "\n\n⭐ Votre satisfaction, notre priorité.",
            "\n\n🔔 Restez connecté pour plus d'actualités !",
            "\n\n💪 Toujours à votre service.",
        ];
        $rewritten .= $suffixes[ array_rand( $suffixes ) ];

        return $rewritten;
    }

    /**
     * Publie un post recyclé via Google_My_Business_API::create_local_post().
     */
    private function publish_post_to_google( string $token, string $location_id, string $content, array $source_post ): bool {
        if ( ! class_exists( __NAMESPACE__ . '\Google_My_Business_API' ) ) {
            return false;
        }

        $api   = new Google_My_Business_API( $token, $location_id );
        $topic = ( ( $source_post['post_type'] ?? '' ) === 'offer' ) ? 'OFFER' : 'STANDARD';

        $result = $api->create_local_post( $content, $topic );

        return $result && ! isset( $result['error'] );
    }

    /**
     * Enregistre le post recyclé dans avi_saas_posts.
     */
    private function save_recycled_post( int $user_id, int $profile_id, array $source, string $content, string $status ): void {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_posts';

        $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $table_exists !== $table ) {
            return;
        }

        $post_type    = $source['post_type'] ?? 'update';
        $title        = 'Recyclé — ' . mb_substr( (string) ( $source['title'] ?? $post_type ), 0, 450 );
        $published_at = ( $status === 'published' ) ? current_time( 'mysql', true ) : null;

        $wpdb->insert( $table, [
            'user_id'      => $user_id,
            'profile_id'   => $profile_id,
            'post_type'    => $post_type,
            'title'        => $title,
            'content'      => $content,
            'status'       => $status,
            'published_at' => $published_at,
            'created_at'   => current_time( 'mysql', true ),
        ], [ '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s' ] );
    }

    // ══════════════════════════════════════════════
    // Écriture avi_security_logs
    // ══════════════════════════════════════════════

    private function write_security_log( int $user_id, string $event_type, int $signal_id, string $status, string $msg_fr, string $msg_en ): void {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_security_logs';

        $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
        if ( $table_exists !== $table ) {
            return;
        }

        $wpdb->insert( $table, [
            'user_id'      => $user_id,
            'event_type'   => $event_type,
            'signal_id'    => $signal_id,
            'status'       => $status,
            'message_json' => wp_json_encode( [ 'fr' => $msg_fr, 'en' => $msg_en ], JSON_UNESCAPED_UNICODE ),
            'created_at'   => current_time( 'mysql', true ),
        ], [ '%d', '%s', '%d', '%s', '%s', '%s' ] );
    }

    // ══════════════════════════════════════════════
    // Logging
    // ══════════════════════════════════════════════

    private function log( string $level, string $message ): void {
        $line = sprintf( "[%s] [%s] %s\n", gmdate( 'Y-m-d H:i:s' ), $level, $message );
        $log_path = $this->get_log_path();
        if ( $log_path ) {
            $dir = dirname( $log_path );
            if ( ! is_dir( $dir ) ) {
                @wp_mkdir_p( $dir );
            }
            @file_put_contents( $log_path, $line, FILE_APPEND | LOCK_EX );
        }
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            error_log( '[AVI Maintainers] [' . $level . '] ' . $message );
        }
    }

    private function get_log_path(): string {
        $plugin_dir = defined( 'AVISION_PLUGIN_DIR' ) ? AVISION_PLUGIN_DIR : ( dirname( __DIR__ ) . '/' );
        return $plugin_dir . self::LOG_FILE;
    }
}
